ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/Makefile
new file mode 100644
index 0000000..a1e9b7c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/Makefile
@@ -0,0 +1,60 @@
+CFLAGS += -DHOSTAPD
+CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_ETH_P_OUI
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_WPS
+CFLAGS += -DCONFIG_PROXYARP
+CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_AIRTIME_POLICY
+
+LIB_OBJS= \
+ accounting.o \
+ ap_config.o \
+ ap_drv_ops.o \
+ ap_list.o \
+ ap_mlme.o \
+ airtime_policy.o \
+ authsrv.o \
+ beacon.o \
+ bss_load.o \
+ ctrl_iface_ap.o \
+ dfs.o \
+ dhcp_snoop.o \
+ drv_callbacks.o \
+ eap_user_db.o \
+ eth_p_oui.o \
+ gas_serv.o \
+ hostapd.o \
+ hs20.o \
+ hw_features.o \
+ ieee802_11_auth.o \
+ ieee802_11.o \
+ ieee802_11_ht.o \
+ ieee802_11_shared.o \
+ ieee802_11_vht.o \
+ ieee802_1x.o \
+ neighbor_db.o \
+ ndisc_snoop.o \
+ p2p_hostapd.o \
+ pmksa_cache_auth.o \
+ preauth_auth.o \
+ rrm.o \
+ sta_info.o \
+ tkip_countermeasures.o \
+ utils.o \
+ vlan.o \
+ vlan_ifconfig.o \
+ vlan_init.o \
+ wmm.o \
+ wnm_ap.o \
+ wpa_auth.o \
+ wpa_auth_ft.o \
+ wpa_auth_glue.o \
+ wpa_auth_ie.o \
+ wps_hostapd.o \
+ x_snoop.o
+
+include ../lib.rules
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.c
new file mode 100644
index 0000000..9fc1886
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.c
@@ -0,0 +1,547 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "accounting.h"
+
+
+/* Default interval in seconds for polling TX/RX octets from the driver if
+ * STA is not using interim accounting. This detects wrap arounds for
+ * input/output octets and updates Acct-{Input,Output}-Gigawords. */
+#define ACCT_DEFAULT_UPDATE_INTERVAL 300
+
+static void accounting_sta_interim(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+
+static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ int status_type)
+{
+ struct radius_msg *msg;
+ char buf[128];
+ u8 *val;
+ size_t len;
+ int i;
+ struct wpabuf *b;
+ struct os_time now;
+
+ msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+ radius_client_get_id(hapd->radius));
+ if (msg == NULL) {
+ wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
+ return NULL;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+ status_type)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
+ goto fail;
+ }
+
+ if (sta) {
+ if (!hostapd_config_get_radius_attr(
+ hapd->conf->radius_acct_req_attr,
+ RADIUS_ATTR_ACCT_AUTHENTIC) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+ hapd->conf->ieee802_1x ?
+ RADIUS_ACCT_AUTHENTIC_RADIUS :
+ RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
+ goto fail;
+ }
+
+ /* Use 802.1X identity if available */
+ val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+
+ /* Use RADIUS ACL identity if 802.1X provides no identity */
+ if (!val && sta->identity) {
+ val = (u8 *) sta->identity;
+ len = os_strlen(sta->identity);
+ }
+
+ /* Use STA MAC if neither 802.1X nor RADIUS ACL provided
+ * identity */
+ if (!val) {
+ os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ val = (u8 *) buf;
+ len = os_strlen(buf);
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
+ len)) {
+ wpa_printf(MSG_INFO, "Could not add User-Name");
+ goto fail;
+ }
+ }
+
+ if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
+ msg) < 0)
+ goto fail;
+
+ if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
+ goto fail;
+
+ if (sta) {
+ for (i = 0; ; i++) {
+ val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
+ i);
+ if (val == NULL)
+ break;
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
+ val, len)) {
+ wpa_printf(MSG_INFO, "Could not add Class");
+ goto fail;
+ }
+ }
+
+ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (b &&
+ !radius_msg_add_attr(msg,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ wpabuf_head(b), wpabuf_len(b))) {
+ wpa_printf(MSG_ERROR, "Could not add CUI");
+ goto fail;
+ }
+
+ if (!b && sta->radius_cui &&
+ !radius_msg_add_attr(msg,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ (u8 *) sta->radius_cui,
+ os_strlen(sta->radius_cui))) {
+ wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
+ goto fail;
+ }
+
+ if (sta->ipaddr &&
+ !radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_FRAMED_IP_ADDRESS,
+ be_to_host32(sta->ipaddr))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add Framed-IP-Address");
+ goto fail;
+ }
+ }
+
+ os_get_time(&now);
+ if (now.sec > 1000000000 &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+ now.sec)) {
+ wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
+ goto fail;
+ }
+
+ /*
+ * Add Acct-Delay-Time with zero value for the first transmission. This
+ * will be updated within radius_client.c when retransmitting the frame.
+ */
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
+ goto fail;
+ }
+
+ return msg;
+
+ fail:
+ radius_msg_free(msg);
+ return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostap_sta_driver_data *data)
+{
+ if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
+ return -1;
+
+ if (!data->bytes_64bit) {
+ /* Extend 32-bit counters from the driver to 64-bit counters */
+ if (sta->last_rx_bytes_lo > data->rx_bytes)
+ sta->last_rx_bytes_hi++;
+ sta->last_rx_bytes_lo = data->rx_bytes;
+
+ if (sta->last_tx_bytes_lo > data->tx_bytes)
+ sta->last_tx_bytes_hi++;
+ sta->last_tx_bytes_lo = data->tx_bytes;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
+ data->rx_bytes, sta->last_rx_bytes_hi,
+ sta->last_rx_bytes_lo,
+ data->tx_bytes, sta->last_tx_bytes_hi,
+ sta->last_tx_bytes_lo,
+ data->bytes_64bit);
+
+ return 0;
+}
+
+
+static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ int interval;
+
+ if (sta->acct_interim_interval) {
+ accounting_sta_interim(hapd, sta);
+ interval = sta->acct_interim_interval;
+ } else {
+ struct hostap_sta_driver_data data;
+ accounting_sta_update_stats(hapd, sta, &data);
+ interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+ }
+
+ eloop_register_timeout(interval, 0, accounting_interim_update,
+ hapd, sta);
+}
+
+
+/**
+ * accounting_sta_start - Start STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct radius_msg *msg;
+ int interval;
+
+ if (sta->acct_session_started)
+ return;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "starting accounting session %016llX",
+ (unsigned long long) sta->acct_session_id);
+
+ os_get_reltime(&sta->acct_session_start);
+ sta->last_rx_bytes_hi = 0;
+ sta->last_rx_bytes_lo = 0;
+ sta->last_tx_bytes_hi = 0;
+ sta->last_tx_bytes_lo = 0;
+ hostapd_drv_sta_clear_stats(hapd, sta->addr);
+
+ if (!hapd->conf->radius->acct_server)
+ return;
+
+ if (sta->acct_interim_interval)
+ interval = sta->acct_interim_interval;
+ else
+ interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+ eloop_register_timeout(interval, 0, accounting_interim_update,
+ hapd, sta);
+
+ msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
+ if (msg &&
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+ radius_msg_free(msg);
+
+ sta->acct_session_started = 1;
+}
+
+
+static void accounting_sta_report(struct hostapd_data *hapd,
+ struct sta_info *sta, int stop)
+{
+ struct radius_msg *msg;
+ int cause = sta->acct_terminate_cause;
+ struct hostap_sta_driver_data data;
+ struct os_reltime now_r, diff;
+ u64 bytes;
+
+ if (!hapd->conf->radius->acct_server)
+ return;
+
+ msg = accounting_msg(hapd, sta,
+ stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
+ RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
+ if (!msg) {
+ wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
+ return;
+ }
+
+ os_get_reltime(&now_r);
+ os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+ diff.sec)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
+ goto fail;
+ }
+
+ if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_PACKETS,
+ data.rx_packets)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+ data.tx_packets)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
+ goto fail;
+ }
+ if (data.bytes_64bit)
+ bytes = data.rx_bytes;
+ else
+ bytes = ((u64) sta->last_rx_bytes_hi << 32) |
+ sta->last_rx_bytes_lo;
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_OCTETS,
+ (u32) bytes)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+ (u32) (bytes >> 32))) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
+ goto fail;
+ }
+ if (data.bytes_64bit)
+ bytes = data.tx_bytes;
+ else
+ bytes = ((u64) sta->last_tx_bytes_hi << 32) |
+ sta->last_tx_bytes_lo;
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+ (u32) bytes)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+ (u32) (bytes >> 32))) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
+ goto fail;
+ }
+ }
+
+ if (eloop_terminated())
+ cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+ if (stop && cause &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+ cause)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
+ goto fail;
+ }
+
+ if (radius_client_send(hapd->radius, msg,
+ stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+ sta->addr) < 0)
+ goto fail;
+ return;
+
+ fail:
+ radius_msg_free(msg);
+}
+
+
+/**
+ * accounting_sta_interim - Send a interim STA accounting report
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+static void accounting_sta_interim(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ if (sta->acct_session_started)
+ accounting_sta_report(hapd, sta, 0);
+}
+
+
+/**
+ * accounting_sta_stop - Stop STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->acct_session_started) {
+ accounting_sta_report(hapd, sta, 1);
+ eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "stopped accounting session %016llX",
+ (unsigned long long) sta->acct_session_id);
+ sta->acct_session_started = 0;
+ }
+}
+
+
+int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ return radius_gen_session_id((u8 *) &sta->acct_session_id,
+ sizeof(sta->acct_session_id));
+}
+
+
+/**
+ * accounting_receive - Process the RADIUS frames from Accounting Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+accounting_receive(struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+ wpa_printf(MSG_INFO, "Unknown RADIUS message code");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+ wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ return RADIUS_RX_PROCESSED;
+}
+
+
+static void accounting_report_state(struct hostapd_data *hapd, int on)
+{
+ struct radius_msg *msg;
+
+ if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
+ return;
+
+ /* Inform RADIUS server that accounting will start/stop so that the
+ * server can close old accounting sessions. */
+ msg = accounting_msg(hapd, NULL,
+ on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
+ RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
+ if (!msg)
+ return;
+
+ if (hapd->acct_session_id) {
+ char buf[20];
+
+ os_snprintf(buf, sizeof(buf), "%016llX",
+ (unsigned long long) hapd->acct_session_id);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) buf, os_strlen(buf)))
+ wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+ }
+
+ if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+ radius_msg_free(msg);
+}
+
+
+static void accounting_interim_error_cb(const u8 *addr, void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ unsigned int i, wait_time;
+ int res;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return;
+ sta->acct_interim_errors++;
+ if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
+ wpa_printf(MSG_DEBUG,
+ "Interim RADIUS accounting update failed for " MACSTR
+ " - too many errors, abandon this interim accounting update",
+ MAC2STR(addr));
+ sta->acct_interim_errors = 0;
+ /* Next update will be tried after normal update interval */
+ return;
+ }
+
+ /*
+ * Use a shorter update interval as an improved retransmission mechanism
+ * for failed interim accounting updates. This allows the statistics to
+ * be updated for each retransmission.
+ *
+ * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
+ * Schedule the first retry attempt immediately and every following one
+ * with exponential backoff.
+ */
+ if (sta->acct_interim_errors == 1) {
+ wait_time = 0;
+ } else {
+ wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
+ for (i = 1; i < sta->acct_interim_errors; i++)
+ wait_time *= 2;
+ }
+ res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
+ hapd, sta);
+ if (res == 1)
+ wpa_printf(MSG_DEBUG,
+ "Interim RADIUS accounting update failed for " MACSTR
+ " (error count: %u) - schedule next update in %u seconds",
+ MAC2STR(addr), sta->acct_interim_errors, wait_time);
+ else if (res == 0)
+ wpa_printf(MSG_DEBUG,
+ "Interim RADIUS accounting update failed for " MACSTR
+ " (error count: %u)", MAC2STR(addr),
+ sta->acct_interim_errors);
+ else
+ wpa_printf(MSG_DEBUG,
+ "Interim RADIUS accounting update failed for " MACSTR
+ " (error count: %u) - no timer found", MAC2STR(addr),
+ sta->acct_interim_errors);
+}
+
+
+/**
+ * accounting_init: Initialize accounting
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int accounting_init(struct hostapd_data *hapd)
+{
+ if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
+ sizeof(hapd->acct_session_id)) < 0)
+ return -1;
+
+ if (radius_client_register(hapd->radius, RADIUS_ACCT,
+ accounting_receive, hapd))
+ return -1;
+ radius_client_set_interim_error_cb(hapd->radius,
+ accounting_interim_error_cb, hapd);
+
+ accounting_report_state(hapd, 1);
+
+ return 0;
+}
+
+
+/**
+ * accounting_deinit: Deinitialize accounting
+ * @hapd: hostapd BSS data
+ */
+void accounting_deinit(struct hostapd_data *hapd)
+{
+ accounting_report_state(hapd, 0);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.h
new file mode 100644
index 0000000..de5a33f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.h
@@ -0,0 +1,45 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACCOUNTING_H
+#define ACCOUNTING_H
+
+#ifdef CONFIG_NO_ACCOUNTING
+static inline int accounting_sta_get_id(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return 0;
+}
+
+static inline void accounting_sta_start(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_stop(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+static inline int accounting_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void accounting_deinit(struct hostapd_data *hapd)
+{
+}
+#else /* CONFIG_NO_ACCOUNTING */
+int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+int accounting_init(struct hostapd_data *hapd);
+void accounting_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_NO_ACCOUNTING */
+
+#endif /* ACCOUNTING_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.c
new file mode 100644
index 0000000..da13d48
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.c
@@ -0,0 +1,1385 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <math.h>
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "hw_features.h"
+#include "acs.h"
+
+/*
+ * Automatic Channel Selection
+ * ===========================
+ *
+ * More info at
+ * ------------
+ * http://wireless.kernel.org/en/users/Documentation/acs
+ *
+ * How to use
+ * ----------
+ * - make sure you have CONFIG_ACS=y in hostapd's .config
+ * - use channel=0 or channel=acs to enable ACS
+ *
+ * How does it work
+ * ----------------
+ * 1. passive scans are used to collect survey data
+ * (it is assumed that scan trigger collection of survey data in driver)
+ * 2. interference factor is calculated for each channel
+ * 3. ideal channel is picked depending on channel width by using adjacent
+ * channel interference factors
+ *
+ * Known limitations
+ * -----------------
+ * - Current implementation depends heavily on the amount of time willing to
+ * spend gathering survey data during hostapd startup. Short traffic bursts
+ * may be missed and a suboptimal channel may be picked.
+ * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
+ *
+ * Todo / Ideas
+ * ------------
+ * - implement other interference computation methods
+ * - BSS/RSSI based
+ * - spectral scan based
+ * (should be possibly to hook this up with current ACS scans)
+ * - add wpa_supplicant support (for P2P)
+ * - collect a histogram of interference over time allowing more educated
+ * guess about an ideal channel (perhaps CSA could be used to migrate AP to a
+ * new "better" channel while running)
+ * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
+ * when choosing the ideal channel
+ *
+ * Survey interference factor implementation details
+ * -------------------------------------------------
+ * Generic interference_factor in struct hostapd_channel_data is used.
+ *
+ * The survey interference factor is defined as the ratio of the
+ * observed busy time over the time we spent on the channel,
+ * this value is then amplified by the observed noise floor on
+ * the channel in comparison to the lowest noise floor observed
+ * on the entire band.
+ *
+ * This corresponds to:
+ * ---
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * ---
+ *
+ * The coefficient of 2 reflects the way power in "far-field"
+ * radiation decreases as the square of distance from the antenna [1].
+ * What this does is it decreases the observed busy time ratio if the
+ * noise observed was low but increases it if the noise was high,
+ * proportionally to the way "far field" radiation changes over
+ * distance.
+ *
+ * If channel busy time is not available the fallback is to use channel RX time.
+ *
+ * Since noise floor is in dBm it is necessary to convert it into Watts so that
+ * combined channel interference (e.g., HT40, which uses two channels) can be
+ * calculated easily.
+ * ---
+ * (busy time - tx time) / (active time - tx time) *
+ * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * However to account for cases where busy/rx time is 0 (channel load is then
+ * 0%) channel noise floor signal power is combined into the equation so a
+ * channel with lower noise floor is preferred. The equation becomes:
+ * ---
+ * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
+ * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * All this "interference factor" is purely subjective and only time
+ * will tell how usable this is. By using the minimum noise floor we
+ * remove any possible issues due to card calibration. The computation
+ * of the interference factor then is dependent on what the card itself
+ * picks up as the minimum noise, not an actual real possible card
+ * noise value.
+ *
+ * Total interference computation details
+ * --------------------------------------
+ * The above channel interference factor is calculated with no respect to
+ * target operational bandwidth.
+ *
+ * To find an ideal channel the above data is combined by taking into account
+ * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
+ * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
+ * on 5 GHz.
+ *
+ * Each valid and possible channel spec (i.e., channel + width) is taken and its
+ * interference factor is computed by summing up interferences of each channel
+ * it overlaps. The one with least total interference is picked up.
+ *
+ * Note: This implies base channel interference factor must be non-negative
+ * allowing easy summing up.
+ *
+ * Example ACS analysis printout
+ * -----------------------------
+ *
+ * ACS: Trying survey-based ACS
+ * ACS: Survey analysis for channel 1 (2412 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
+ * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
+ * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS: * interference factor average: 0.0557166
+ * ACS: Survey analysis for channel 2 (2417 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
+ * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
+ * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
+ * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS: * interference factor average: 0.050832
+ * ACS: Survey analysis for channel 3 (2422 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: * interference factor average: 0.0148838
+ * ACS: Survey analysis for channel 4 (2427 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: * interference factor average: 0.0160801
+ * ACS: Survey analysis for channel 5 (2432 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
+ * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
+ * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
+ * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
+ * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS: * interference factor average: 0.232244
+ * ACS: Survey analysis for channel 6 (2437 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
+ * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
+ * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
+ * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS: * interference factor average: 0.232298
+ * ACS: Survey analysis for channel 7 (2442 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
+ * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
+ * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS: * interference factor average: 0.195031
+ * ACS: Survey analysis for channel 8 (2447 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
+ * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
+ * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
+ * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
+ * ACS: * interference factor average: 0.0865885
+ * ACS: Survey analysis for channel 9 (2452 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
+ * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.00993022
+ * ACS: Survey analysis for channel 10 (2457 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.0136033
+ * ACS: Survey analysis for channel 11 (2462 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
+ * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
+ * ACS: * interference factor average: 0.0271605
+ * ACS: Survey analysis for channel 12 (2467 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
+ * ACS: * interference factor average: 0.0148992
+ * ACS: Survey analysis for channel 13 (2472 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
+ * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.0260179
+ * ACS: Survey analysis for selected bandwidth 20MHz
+ * ACS: * channel 1: total interference = 0.121432
+ * ACS: * channel 2: total interference = 0.137512
+ * ACS: * channel 3: total interference = 0.369757
+ * ACS: * channel 4: total interference = 0.546338
+ * ACS: * channel 5: total interference = 0.690538
+ * ACS: * channel 6: total interference = 0.762242
+ * ACS: * channel 7: total interference = 0.756092
+ * ACS: * channel 8: total interference = 0.537451
+ * ACS: * channel 9: total interference = 0.332313
+ * ACS: * channel 10: total interference = 0.152182
+ * ACS: * channel 11: total interference = 0.0916111
+ * ACS: * channel 12: total interference = 0.0816809
+ * ACS: * channel 13: total interference = 0.0680776
+ * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
+ *
+ * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+ */
+
+enum bw_type {
+ ACS_BW40,
+ ACS_BW80,
+ ACS_BW160,
+};
+
+struct bw_item {
+ int first;
+ int last;
+ int center_chan;
+};
+
+static const struct bw_item bw_40[] = {
+ { 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
+ { 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
+ { 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+ { 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
+ { 5825, 5845, 167 }, { 5865, 5885, 175 },
+ { 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
+ { 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
+ { 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
+ { 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
+ { 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
+ { 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
+ { 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
+ { 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
+ { 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
+ { 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
+};
+static const struct bw_item bw_80[] = {
+ { 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
+ { 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
+ { 5825, 5885, 171},
+ { 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
+ { 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
+ { 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
+ { 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
+ { 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
+};
+static const struct bw_item bw_160[] = {
+ { 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
+ { 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
+ { 6435, 6575, 111 }, { 6595, 6735, 143 },
+ { 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+};
+static const struct bw_item *bw_desc[] = {
+ [ACS_BW40] = bw_40,
+ [ACS_BW80] = bw_80,
+ [ACS_BW160] = bw_160,
+};
+
+
+static int acs_request_scan(struct hostapd_iface *iface);
+static int acs_survey_is_sufficient(struct freq_survey *survey);
+
+
+static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey, *tmp;
+
+ if (dl_list_empty(&chan->survey_list))
+ return;
+
+ dl_list_for_each_safe(survey, tmp, &chan->survey_list,
+ struct freq_survey, list) {
+ dl_list_del(&survey->list);
+ os_free(survey);
+ }
+}
+
+
+static void acs_cleanup_mode(struct hostapd_hw_modes *mode)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
+ acs_clean_chan_surveys(chan);
+
+ dl_list_init(&chan->survey_list);
+ chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+ chan->min_nf = 0;
+ chan->punct_bitmap = 0;
+ }
+}
+
+
+void acs_cleanup(struct hostapd_iface *iface)
+{
+ int i;
+
+ for (i = 0; i < iface->num_hw_features; i++)
+ acs_cleanup_mode(&iface->hw_features[i]);
+
+ iface->chans_surveyed = 0;
+ iface->acs_num_completed_scans = 0;
+}
+
+
+static void acs_fail(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_ERROR, "ACS: Failed to start");
+ acs_cleanup(iface);
+ hostapd_disable_iface(iface);
+}
+
+
+static long double
+acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
+{
+ long double factor, busy, total;
+
+ if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
+ busy = survey->channel_time_busy;
+ else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
+ busy = survey->channel_time_rx;
+ else {
+ wpa_printf(MSG_ERROR, "ACS: Survey data missing");
+ return 0;
+ }
+
+ total = survey->channel_time;
+
+ if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
+ busy -= survey->channel_time_tx;
+ total -= survey->channel_time_tx;
+ }
+
+ /* TODO: figure out the best multiplier for noise floor base */
+ factor = pow(10, survey->nf / 5.0L) +
+ (total ? (busy / total) : 0) *
+ pow(2, pow(10, (long double) survey->nf / 10.0L) -
+ pow(10, (long double) min_nf / 10.0L));
+
+ return factor;
+}
+
+
+static void
+acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey;
+ unsigned int i = 0;
+ long double int_factor = 0;
+ unsigned count = 0;
+
+ if (dl_list_empty(&chan->survey_list) ||
+ (chan->flag & HOSTAPD_CHAN_DISABLED))
+ return;
+
+ chan->interference_factor = 0;
+
+ dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+ {
+ i++;
+
+ if (!acs_survey_is_sufficient(survey)) {
+ wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
+ continue;
+ }
+
+ count++;
+ int_factor = acs_survey_interference_factor(survey,
+ iface->lowest_nf);
+ chan->interference_factor += int_factor;
+ wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
+ i, chan->min_nf, int_factor,
+ survey->nf, (unsigned long) survey->channel_time,
+ (unsigned long) survey->channel_time_busy,
+ (unsigned long) survey->channel_time_rx);
+ }
+
+ if (count)
+ chan->interference_factor /= count;
+}
+
+
+static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+ enum bw_type bw)
+{
+ unsigned int i = 0;
+
+ while (bw_desc[bw][i].first != -1) {
+ if (chan->freq == bw_desc[bw][i].first)
+ return true;
+ i++;
+ }
+
+ return false;
+}
+
+
+static int acs_get_bw_center_chan(int freq, enum bw_type bw)
+{
+ unsigned int i = 0;
+
+ while (bw_desc[bw][i].first != -1) {
+ if (freq >= bw_desc[bw][i].first &&
+ freq <= bw_desc[bw][i].last)
+ return bw_desc[bw][i].center_chan;
+ i++;
+ }
+
+ return 0;
+}
+
+
+static int acs_survey_is_sufficient(struct freq_survey *survey)
+{
+ if (!(survey->filled & SURVEY_HAS_NF)) {
+ survey->nf = -95;
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing noise floor",
+ survey->freq);
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
+ survey->channel_time = 0;
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing channel time",
+ survey->freq);
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+ !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+ survey->freq);
+ }
+
+ return 1;
+}
+
+
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey;
+ int ret = -1;
+
+ dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+ {
+ if (acs_survey_is_sufficient(survey)) {
+ ret = 1;
+ break;
+ }
+ ret = 0;
+ }
+
+ if (ret == -1)
+ ret = 1; /* no survey list entries */
+
+ if (!ret) {
+ wpa_printf(MSG_INFO,
+ "ACS: Channel %d has insufficient survey data",
+ chan->chan);
+ }
+
+ return ret;
+}
+
+
+static int acs_surveys_are_sufficient_mode(struct hostapd_hw_modes *mode)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ acs_survey_list_is_sufficient(chan))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode) &&
+ acs_surveys_are_sufficient_mode(mode))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+ return !dl_list_empty(&chan->survey_list) &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ acs_survey_list_is_sufficient(chan);
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ if (!iface->conf->acs_ch_list.num)
+ return 1;
+
+ return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+static int is_in_freqlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ if (!iface->conf->acs_freq_list.num)
+ return 1;
+
+ return freq_range_list_includes(&iface->conf->acs_freq_list,
+ chan->freq);
+}
+
+
+static void acs_survey_mode_interference_factor(
+ struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (!acs_usable_chan(chan))
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs)
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
+ if (chan->max_tx_power < iface->conf->min_tx_power)
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
+ chan->chan, chan->freq);
+
+ acs_survey_chan_interference_factor(iface, chan);
+
+ wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
+ chan->interference_factor);
+ }
+}
+
+
+static void acs_survey_all_chans_interference_factor(
+ struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode))
+ acs_survey_mode_interference_factor(iface, mode);
+ }
+}
+
+
+static struct hostapd_channel_data *
+acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq)
+{
+ struct hostapd_channel_data *chan;
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ if (chan->freq == freq)
+ return chan;
+ }
+
+ return NULL;
+}
+
+
+static enum hostapd_hw_mode
+acs_find_mode(struct hostapd_iface *iface, int freq)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode)) {
+ chan = acs_find_chan_mode(mode, freq);
+ if (chan)
+ return mode->mode;
+ }
+ }
+
+ return HOSTAPD_MODE_IEEE80211ANY;
+}
+
+
+static struct hostapd_channel_data *
+acs_find_chan(struct hostapd_iface *iface, int freq)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode)) {
+ chan = acs_find_chan_mode(mode, freq);
+ if (chan)
+ return chan;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int is_24ghz_mode(enum hostapd_hw_mode mode)
+{
+ return mode == HOSTAPD_MODE_IEEE80211B ||
+ mode == HOSTAPD_MODE_IEEE80211G;
+}
+
+
+static int is_common_24ghz_chan(int chan)
+{
+ return chan == 1 || chan == 6 || chan == 11;
+}
+
+
+#ifndef ACS_ADJ_WEIGHT
+#define ACS_ADJ_WEIGHT 0.85
+#endif /* ACS_ADJ_WEIGHT */
+
+#ifndef ACS_NEXT_ADJ_WEIGHT
+#define ACS_NEXT_ADJ_WEIGHT 0.55
+#endif /* ACS_NEXT_ADJ_WEIGHT */
+
+#ifndef ACS_24GHZ_PREFER_1_6_11
+/*
+ * Select commonly used channels 1, 6, 11 by default even if a neighboring
+ * channel has a smaller interference factor as long as it is not better by more
+ * than this multiplier.
+ */
+#define ACS_24GHZ_PREFER_1_6_11 0.8
+#endif /* ACS_24GHZ_PREFER_1_6_11 */
+
+
+#ifdef CONFIG_IEEE80211BE
+static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode, u32 bw,
+ int n_chans,
+ struct hostapd_channel_data *chan,
+ long double factor,
+ int index_primary)
+{
+ struct hostapd_config *conf = iface->conf;
+ struct hostapd_channel_data *adj_chan = NULL, *first_chan = chan;
+ int i;
+ long double threshold;
+
+ /*
+ * If threshold is 0 or user configured puncturing pattern is
+ * available then don't add additional puncturing.
+ */
+ if (!conf->punct_acs_threshold || conf->punct_bitmap)
+ return;
+
+ if (is_24ghz_mode(mode->mode) || bw < 80)
+ return;
+
+ threshold = factor * conf->punct_acs_threshold / 100;
+ for (i = 0; i < n_chans; i++) {
+ int adj_freq;
+
+ if (i == index_primary)
+ continue; /* Cannot puncture primary channel */
+
+ if (i > index_primary)
+ adj_freq = chan->freq + (i - index_primary) * 20;
+ else
+ adj_freq = chan->freq - (index_primary - i) * 20;
+
+ adj_chan = acs_find_chan(iface, adj_freq);
+ if (!adj_chan) {
+ chan->punct_bitmap = 0;
+ return;
+ }
+
+ if (i == 0)
+ first_chan = adj_chan;
+
+ if (adj_chan->interference_factor > threshold)
+ chan->punct_bitmap |= BIT(i);
+ }
+
+ if (!is_punct_bitmap_valid(bw, (chan->freq - first_chan->freq) / 20,
+ chan->punct_bitmap))
+ chan->punct_bitmap = 0;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void
+acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode,
+ int n_chans, u32 bw,
+ struct hostapd_channel_data **rand_chan,
+ struct hostapd_channel_data **ideal_chan,
+ long double *ideal_factor)
+{
+ struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+ long double factor;
+ int i, j;
+ unsigned int k;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ double total_weight;
+ struct acs_bias *bias, tmp_bias;
+ bool update_best = true;
+
+ best = chan = &mode->channels[i];
+
+ /* Since in the current ACS implementation the first channel is
+ * always a primary channel, skip channels not available as
+ * primary until more sophisticated channel selection is
+ * implemented.
+ *
+ * If this implementation is changed to allow any channel in
+ * the bandwidth to be the primary one, the last parameter to
+ * acs_update_puncturing_bitmap() should be changed to the index
+ * of the primary channel
+ */
+ if (!chan_pri_allowed(chan))
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs)
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
+ if (chan->max_tx_power < iface->conf->min_tx_power)
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
+ if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: BW %u is not supported",
+ chan->chan, bw);
+ continue;
+ }
+
+ /* HT40 on 5 GHz has a limited set of primary channels as per
+ * 11n Annex J */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ ((iface->conf->ieee80211n &&
+ iface->conf->secondary_channel) ||
+ is_6ghz_freq(chan->freq)) &&
+ !acs_usable_bw_chan(chan, ACS_BW40)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
+ chan->chan);
+ continue;
+ }
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_80MHZ &&
+ !acs_usable_bw_chan(chan, ACS_BW80)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
+ chan->chan);
+ continue;
+ }
+
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_160MHZ &&
+ !acs_usable_bw_chan(chan, ACS_BW160)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
+ chan->chan);
+ continue;
+ }
+ }
+
+ factor = 0;
+ if (acs_usable_chan(chan))
+ factor = chan->interference_factor;
+ total_weight = 1;
+
+ for (j = 1; j < n_chans; j++) {
+ adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ if (!adj_chan)
+ break;
+
+ if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+ chan->chan, adj_chan->chan, bw);
+ break;
+ }
+
+ if (acs_usable_chan(adj_chan)) {
+ factor += adj_chan->interference_factor;
+ total_weight += 1;
+ } else {
+ update_best = false;
+ }
+
+ /* find the best channel in this segment */
+ if (update_best &&
+ adj_chan->interference_factor <
+ best->interference_factor)
+ best = adj_chan;
+ }
+
+ if (j != n_chans) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+ chan->chan);
+ continue;
+ }
+
+ /* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+ * crowded primary channel if one was found in the segment */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ chan != best) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+ best->chan, chan->chan,
+ chan->interference_factor,
+ best->interference_factor);
+ chan = best;
+ }
+
+ /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+ * channel interference factor. */
+ if (is_24ghz_mode(mode->mode)) {
+ for (j = 0; j < n_chans; j++) {
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) - 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_ADJ_WEIGHT;
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) - 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_NEXT_ADJ_WEIGHT;
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) + 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_ADJ_WEIGHT;
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) + 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_NEXT_ADJ_WEIGHT;
+ }
+ }
+ }
+
+ factor /= total_weight;
+
+ bias = NULL;
+ if (iface->conf->acs_chan_bias) {
+ for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
+ bias = &iface->conf->acs_chan_bias[k];
+ if (bias->channel == chan->chan)
+ break;
+ bias = NULL;
+ }
+ } else if (is_24ghz_mode(mode->mode) &&
+ is_common_24ghz_chan(chan->chan)) {
+ tmp_bias.channel = chan->chan;
+ tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
+ bias = &tmp_bias;
+ }
+
+ if (bias) {
+ factor *= bias->bias;
+ wpa_printf(MSG_DEBUG,
+ "ACS: * channel %d: total interference = %Lg (%f bias)",
+ chan->chan, factor, bias->bias);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "ACS: * channel %d: total interference = %Lg",
+ chan->chan, factor);
+ }
+
+ if (acs_usable_chan(chan) &&
+ (!*ideal_chan || factor < *ideal_factor)) {
+ /* Reset puncturing bitmap for the previous ideal
+ * channel */
+ if (*ideal_chan)
+ (*ideal_chan)->punct_bitmap = 0;
+
+ *ideal_factor = factor;
+ *ideal_chan = chan;
+
+#ifdef CONFIG_IEEE80211BE
+ if (iface->conf->ieee80211be)
+ acs_update_puncturing_bitmap(iface, mode, bw,
+ n_chans, chan,
+ factor, 0);
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ /* This channel would at least be usable */
+ if (!(*rand_chan))
+ *rand_chan = chan;
+ }
+}
+
+
+/*
+ * At this point it's assumed chan->interference_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *ideal_chan = NULL,
+ *rand_chan = NULL;
+ long double ideal_factor = 0;
+ int i;
+ int n_chans = 1;
+ u32 bw;
+ struct hostapd_hw_modes *mode;
+
+ if (is_6ghz_op_class(iface->conf->op_class)) {
+ bw = op_class_to_bandwidth(iface->conf->op_class);
+ n_chans = bw / 20;
+ goto bw_selected;
+ }
+
+ /* TODO: HT40- support */
+
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel == -1) {
+ wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+ return NULL;
+ }
+
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel)
+ n_chans = 2;
+
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_80MHZ:
+ n_chans = 4;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+ default:
+ break;
+ }
+ }
+
+ bw = num_chan_to_bw(n_chans);
+
+bw_selected:
+ /* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
+
+ wpa_printf(MSG_DEBUG,
+ "ACS: Survey analysis for selected bandwidth %d MHz", bw);
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode))
+ acs_find_ideal_chan_mode(iface, mode, n_chans, bw,
+ &rand_chan, &ideal_chan,
+ &ideal_factor);
+ }
+
+ if (ideal_chan) {
+ wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
+ ideal_chan->chan, ideal_chan->freq, ideal_factor);
+
+#ifdef CONFIG_IEEE80211BE
+ if (iface->conf->punct_acs_threshold)
+ wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
+ ideal_chan->punct_bitmap);
+#endif /* CONFIG_IEEE80211BE */
+
+ return ideal_chan;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (iface->conf->punct_acs_threshold)
+ wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
+ ideal_chan->punct_bitmap);
+#endif /* CONFIG_IEEE80211BE */
+
+ return rand_chan;
+}
+
+
+static void acs_adjust_secondary(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ /* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
+ * ACS can return a secondary channel which is not the first channel of
+ * the segment and we need to adjust. */
+ if (!iface->conf->secondary_channel ||
+ acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+ return;
+
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+
+ for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
+ if (iface->freq == bw_desc[ACS_BW40][i].first)
+ iface->conf->secondary_channel = 1;
+ else if (iface->freq == bw_desc[ACS_BW40][i].last)
+ iface->conf->secondary_channel = -1;
+ }
+}
+
+
+static void acs_adjust_center_freq(struct hostapd_iface *iface)
+{
+ int center;
+
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+ if (iface->conf->secondary_channel &&
+ iface->freq >= 2400 && iface->freq < 2500)
+ center = iface->conf->channel +
+ 2 * iface->conf->secondary_channel;
+ else if (iface->conf->secondary_channel)
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
+ else
+ center = iface->conf->channel;
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+ break;
+ default:
+ /* TODO: How can this be calculated? Adjust
+ * acs_find_ideal_chan() */
+ wpa_printf(MSG_INFO,
+ "ACS: Only VHT20/40/80/160 is supported now");
+ return;
+ }
+
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
+}
+
+
+static int acs_study_survey_based(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
+
+ if (!iface->chans_surveyed) {
+ wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
+ return -1;
+ }
+
+ if (!acs_surveys_are_sufficient(iface)) {
+ wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
+ return -1;
+ }
+
+ acs_survey_all_chans_interference_factor(iface);
+ return 0;
+}
+
+
+static int acs_study_options(struct hostapd_iface *iface)
+{
+ if (acs_study_survey_based(iface) == 0)
+ return 0;
+
+ /* TODO: If no surveys are available/sufficient this is a good
+ * place to fallback to BSS-based ACS */
+
+ return -1;
+}
+
+
+static void acs_study(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *ideal_chan;
+ int err;
+
+ err = acs_study_options(iface);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "ACS: All study options have failed");
+ goto fail;
+ }
+
+ ideal_chan = acs_find_ideal_chan(iface);
+ if (!ideal_chan) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+ err = -1;
+ goto fail;
+ }
+
+ iface->conf->channel = ideal_chan->chan;
+ iface->freq = ideal_chan->freq;
+#ifdef CONFIG_IEEE80211BE
+ iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ acs_adjust_secondary(iface);
+ acs_adjust_center_freq(iface);
+ }
+
+ err = hostapd_select_hw_mode(iface);
+ if (err) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
+ err, iface->freq, iface->conf->channel);
+ err = -1;
+ goto fail;
+ }
+
+ err = 0;
+fail:
+ /*
+ * hostapd_setup_interface_complete() will return -1 on failure,
+ * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
+ */
+ if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
+ acs_cleanup(iface);
+ return;
+ }
+
+ /* This can possibly happen if channel parameters (secondary
+ * channel, center frequencies) are misconfigured */
+ wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
+ acs_fail(iface);
+}
+
+
+static void acs_scan_complete(struct hostapd_iface *iface)
+{
+ int err;
+
+ iface->scan_cb = NULL;
+
+ wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
+ iface->conf->acs_num_scans);
+
+ err = hostapd_drv_get_survey(iface->bss[0], 0);
+ if (err) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
+ goto fail;
+ }
+
+ if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
+ err = acs_request_scan(iface);
+ if (err) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
+ goto fail;
+ }
+
+ return;
+ }
+
+ acs_study(iface);
+ return;
+fail:
+ hostapd_acs_completed(iface, 1);
+ acs_fail(iface);
+}
+
+
+static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode,
+ int *freq)
+{
+ struct hostapd_channel_data *chan;
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+ ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs))
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
+ if (chan->max_tx_power < iface->conf->min_tx_power)
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
+ *freq++ = chan->freq;
+ }
+
+ return freq;
+}
+
+
+static int acs_request_scan(struct hostapd_iface *iface)
+{
+ struct wpa_driver_scan_params params;
+ int i, *freq;
+ int num_channels;
+ struct hostapd_hw_modes *mode;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ num_channels = 0;
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode))
+ num_channels += mode->num_channels;
+ }
+
+ params.freqs = os_calloc(num_channels + 1, sizeof(params.freqs[0]));
+ if (params.freqs == NULL)
+ return -1;
+
+ freq = params.freqs;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (!hostapd_hw_skip_mode(iface, mode))
+ freq = acs_request_scan_add_freqs(iface, mode, freq);
+ }
+
+ *freq = 0;
+
+ if (params.freqs == freq) {
+ wpa_printf(MSG_ERROR, "ACS: No available channels found");
+ os_free(params.freqs);
+ return -1;
+ }
+
+ iface->scan_cb = acs_scan_complete;
+
+ wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
+
+ if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
+ acs_cleanup(iface);
+ os_free(params.freqs);
+ return -1;
+ }
+
+ os_free(params.freqs);
+ return 0;
+}
+
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+ int err;
+
+ wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+ wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+
+ err = hostapd_drv_do_acs(iface->bss[0]);
+ if (err) {
+ if (err == 1)
+ return HOSTAPD_CHAN_INVALID_NO_IR;
+ return HOSTAPD_CHAN_INVALID;
+ }
+
+ return HOSTAPD_CHAN_ACS;
+ }
+
+ if (!iface->current_mode &&
+ iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
+ return HOSTAPD_CHAN_INVALID;
+
+ acs_cleanup(iface);
+
+ if (acs_request_scan(iface) < 0)
+ return HOSTAPD_CHAN_INVALID;
+
+ hostapd_set_state(iface, HAPD_IFACE_ACS);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
+
+ return HOSTAPD_CHAN_ACS;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.h
new file mode 100644
index 0000000..ec84f0e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.h
@@ -0,0 +1,32 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACS_H
+#define ACS_H
+
+#ifdef CONFIG_ACS
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+void acs_cleanup(struct hostapd_iface *iface);
+
+#else /* CONFIG_ACS */
+
+static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
+ return HOSTAPD_CHAN_INVALID;
+}
+
+static inline void acs_cleanup(struct hostapd_iface *iface)
+{
+}
+
+#endif /* CONFIG_ACS */
+
+#endif /* ACS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.c
new file mode 100644
index 0000000..3ec8272
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.c
@@ -0,0 +1,282 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "airtime_policy.h"
+
+/* Idea:
+ * Two modes of airtime enforcement:
+ * 1. Static weights: specify weights per MAC address with a per-BSS default
+ * 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
+ * enforce relative total shares between BSSes.
+ *
+ * - Periodic per-station callback to update queue status.
+ *
+ * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
+ * keep them updated in sta_info.
+ *
+ * - Separate periodic per-bss (or per-iface?) callback to update weights.
+ *
+ * Just need to loop through all interfaces, count sum the active stations (or
+ * should the per-STA callback just adjust that for the BSS?) and calculate new
+ * weights.
+ */
+
+static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
+ unsigned int *sec,
+ unsigned int *usec)
+{
+ unsigned int update_int = iface->conf->airtime_update_interval;
+
+ if (!update_int) {
+ wpa_printf(MSG_ERROR,
+ "Airtime policy: Invalid airtime policy update interval %u",
+ update_int);
+ return -1;
+ }
+
+ *sec = update_int / 1000;
+ *usec = (update_int % 1000) * 1000;
+
+ return 0;
+}
+
+
+static void set_new_backlog_time(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct os_reltime *now)
+{
+ sta->backlogged_until = *now;
+ sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
+ AIRTIME_BACKLOG_EXPIRY_FACTOR;
+ while (sta->backlogged_until.usec >= 1000000) {
+ sta->backlogged_until.sec++;
+ sta->backlogged_until.usec -= 1000000;
+ }
+}
+
+
+static void count_backlogged_sta(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct hostap_sta_driver_data data = {};
+ unsigned int num_backlogged = 0;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
+ continue;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->force_backlog_bytes)
+ data.backlog_bytes = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (data.backlog_bytes > 0)
+ set_new_backlog_time(hapd, sta, &now);
+ if (os_reltime_before(&now, &sta->backlogged_until))
+ num_backlogged++;
+ }
+ hapd->num_backlogged_sta = num_backlogged;
+}
+
+
+static int sta_set_airtime_weight(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ unsigned int weight)
+{
+ int ret = 0;
+
+ if (weight != sta->airtime_weight &&
+ (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
+ return ret;
+
+ sta->airtime_weight = weight;
+ return ret;
+}
+
+
+static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ unsigned int sta_weight = weight;
+
+ if (sta->dyn_airtime_weight)
+ sta_weight = (weight * sta->dyn_airtime_weight) / 256;
+
+ sta_set_airtime_weight(hapd, sta, sta_weight);
+ }
+}
+
+
+static unsigned int get_airtime_quantum(unsigned int max_wt)
+{
+ unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
+
+ if (quantum < AIRTIME_QUANTUM_MIN)
+ quantum = AIRTIME_QUANTUM_MIN;
+ else if (quantum > AIRTIME_QUANTUM_MAX)
+ quantum = AIRTIME_QUANTUM_MAX;
+
+ return quantum;
+}
+
+
+static void update_airtime_weights(void *eloop_data, void *user_data)
+{
+ struct hostapd_iface *iface = eloop_data;
+ struct hostapd_data *bss;
+ unsigned int sec, usec;
+ unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
+ wt_sum = 0;
+ unsigned int quantum;
+ bool all_div_min = true;
+ bool apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
+ int wt, num_bss = 0, max_wt = 0;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ bss = iface->bss[i];
+ if (!bss->started || !bss->conf->airtime_weight)
+ continue;
+
+ count_backlogged_sta(bss);
+ if (!bss->num_backlogged_sta)
+ continue;
+
+ if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
+ num_sta_min = bss->num_backlogged_sta;
+
+ num_sta_prod *= bss->num_backlogged_sta;
+ num_sta_sum += bss->num_backlogged_sta;
+ wt_sum += bss->conf->airtime_weight;
+ num_bss++;
+ }
+
+ if (num_sta_min) {
+ for (i = 0; i < iface->num_bss; i++) {
+ bss = iface->bss[i];
+ if (!bss->started || !bss->conf->airtime_weight)
+ continue;
+
+ /* Check if we can divide all sta numbers by the
+ * smallest number to keep weights as small as possible.
+ * This is a lazy way to avoid having to factor
+ * integers. */
+ if (bss->num_backlogged_sta &&
+ bss->num_backlogged_sta % num_sta_min > 0)
+ all_div_min = false;
+
+ /* If we're in LIMIT mode, we only apply the weight
+ * scaling when the BSS(es) marked as limited would a
+ * larger share than the relative BSS weights indicates
+ * it should. */
+ if (!apply_limit && bss->conf->airtime_limit) {
+ if (bss->num_backlogged_sta * wt_sum >
+ bss->conf->airtime_weight * num_sta_sum)
+ apply_limit = true;
+ }
+ }
+ if (all_div_min)
+ num_sta_prod /= num_sta_min;
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ bss = iface->bss[i];
+ if (!bss->started || !bss->conf->airtime_weight)
+ continue;
+
+ /* We only set the calculated weight if the BSS has active
+ * stations and there are other active interfaces as well -
+ * otherwise we just set a unit weight. This ensures that
+ * the weights are set reasonably when stations transition from
+ * inactive to active. */
+ if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
+ wt = bss->conf->airtime_weight * num_sta_prod /
+ bss->num_backlogged_sta;
+ else
+ wt = 1;
+
+ bss->airtime_weight = wt;
+ if (wt > max_wt)
+ max_wt = wt;
+ }
+
+ quantum = get_airtime_quantum(max_wt);
+
+ for (i = 0; i < iface->num_bss; i++) {
+ bss = iface->bss[i];
+ if (!bss->started || !bss->conf->airtime_weight)
+ continue;
+ set_sta_weights(bss, bss->airtime_weight * quantum);
+ }
+
+ if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+ return;
+
+ eloop_register_timeout(sec, usec, update_airtime_weights, iface,
+ NULL);
+}
+
+
+static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+ struct airtime_sta_weight *wt;
+
+ wt = hapd->conf->airtime_weight_list;
+ while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+ wt = wt->next;
+
+ return wt ? wt->weight : hapd->conf->airtime_weight;
+}
+
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ unsigned int weight;
+
+ if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+ if (sta->dyn_airtime_weight)
+ weight = sta->dyn_airtime_weight;
+ else
+ weight = get_weight_for_sta(hapd, sta->addr);
+ if (weight)
+ return sta_set_airtime_weight(hapd, sta, weight);
+ }
+ return 0;
+}
+
+
+int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+ unsigned int sec, usec;
+
+ if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
+ return 0;
+
+ if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+ return -1;
+
+ eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
+ return 0;
+}
+
+
+void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(update_airtime_weights, iface, NULL);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.h
new file mode 100644
index 0000000..c2a9b00
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.h
@@ -0,0 +1,48 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AIRTIME_POLICY_H
+#define AIRTIME_POLICY_H
+
+struct hostapd_iface;
+
+#ifdef CONFIG_AIRTIME_POLICY
+
+#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
+#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
+
+/* scale quantum so this becomes the effective quantum after applying the max
+ * weight, but never go below min or above max */
+#define AIRTIME_QUANTUM_MIN 8 /* usec */
+#define AIRTIME_QUANTUM_MAX 256 /* usec */
+#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
+int airtime_policy_update_init(struct hostapd_iface *iface);
+void airtime_policy_update_deinit(struct hostapd_iface *iface);
+
+#else /* CONFIG_AIRTIME_POLICY */
+
+static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return -1;
+}
+
+static inline int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+ return -1;
+}
+
+static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+}
+
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#endif /* AIRTIME_POLICY_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.c
new file mode 100644
index 0000000..61cfcca
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.c
@@ -0,0 +1,1769 @@
+/*
+ * hostapd / Configuration helper functions
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "radius/radius_client.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "common/eapol_common.h"
+#include "common/dhcp.h"
+#include "common/sae.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "airtime_policy.h"
+#include "ap_config.h"
+
+
+static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
+{
+ struct hostapd_vlan *vlan, *prev;
+
+ vlan = bss->vlan;
+ prev = NULL;
+ while (vlan) {
+ prev = vlan;
+ vlan = vlan->next;
+ os_free(prev);
+ }
+
+ bss->vlan = NULL;
+}
+
+
+#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
+#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
+#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
+
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+{
+ dl_list_init(&bss->anqp_elem);
+
+ bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+ bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+ bss->logger_syslog = (unsigned int) -1;
+ bss->logger_stdout = (unsigned int) -1;
+
+#ifdef CONFIG_WEP
+ bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
+
+ bss->wep_rekeying_period = 300;
+ /* use key0 in individual key and key1 in broadcast key */
+ bss->broadcast_key_idx_min = 1;
+ bss->broadcast_key_idx_max = 2;
+#else /* CONFIG_WEP */
+ bss->auth_algs = WPA_AUTH_ALG_OPEN;
+#endif /* CONFIG_WEP */
+ bss->eap_reauth_period = 3600;
+
+ bss->wpa_group_rekey = 600;
+ bss->wpa_gmk_rekey = 86400;
+ bss->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
+ bss->wpa_group_update_count = 4;
+ bss->wpa_pairwise_update_count = 4;
+ bss->wpa_disable_eapol_key_retries =
+ DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_NO_TKIP
+ bss->wpa_pairwise = WPA_CIPHER_CCMP;
+ bss->wpa_group = WPA_CIPHER_CCMP;
+#else /* CONFIG_NO_TKIP */
+ bss->wpa_pairwise = WPA_CIPHER_TKIP;
+ bss->wpa_group = WPA_CIPHER_TKIP;
+#endif /* CONFIG_NO_TKIP */
+ bss->rsn_pairwise = 0;
+
+ bss->max_num_sta = MAX_STA_COUNT;
+
+ bss->dtim_period = 2;
+
+ bss->radius_server_auth_port = 1812;
+ bss->eap_sim_db_timeout = 1;
+ bss->eap_sim_id = 3;
+ bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+ bss->eapol_version = EAPOL_VERSION;
+
+ bss->max_listen_interval = 65535;
+
+ bss->pwd_group = 19; /* ECC: GF(p=256) */
+
+ bss->assoc_sa_query_max_timeout = 1000;
+ bss->assoc_sa_query_retry_timeout = 201;
+ bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+#ifdef EAP_SERVER_FAST
+ /* both anonymous and authenticated provisioning */
+ bss->eap_fast_prov = 3;
+ bss->pac_key_lifetime = 7 * 24 * 60 * 60;
+ bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
+#endif /* EAP_SERVER_FAST */
+
+ /* Set to -1 as defaults depends on HT in setup */
+ bss->wmm_enabled = -1;
+
+#ifdef CONFIG_IEEE80211R_AP
+ bss->ft_over_ds = 1;
+ bss->rkh_pos_timeout = 86400;
+ bss->rkh_neg_timeout = 60;
+ bss->rkh_pull_timeout = 1000;
+ bss->rkh_pull_retries = 4;
+ bss->r0_key_lifetime = 1209600;
+#endif /* CONFIG_IEEE80211R_AP */
+
+ bss->radius_das_time_window = 300;
+
+ bss->anti_clogging_threshold = 5;
+ bss->sae_sync = 5;
+
+ bss->gas_frag_limit = 1400;
+
+#ifdef CONFIG_FILS
+ dl_list_init(&bss->fils_realms);
+ bss->fils_hlp_wait_time = 30;
+ bss->dhcp_server_port = DHCP_SERVER_PORT;
+ bss->dhcp_relay_port = DHCP_SERVER_PORT;
+ bss->fils_discovery_min_int = 20;
+#endif /* CONFIG_FILS */
+
+ bss->broadcast_deauth = 1;
+
+#ifdef CONFIG_MBO
+ bss->mbo_cell_data_conn_pref = -1;
+#endif /* CONFIG_MBO */
+
+ /* Disable TLS v1.3 by default for now to avoid interoperability issue.
+ * This can be enabled by default once the implementation has been fully
+ * completed and tested with other implementations. */
+ bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
+
+ bss->max_auth_rounds = 100;
+ bss->max_auth_rounds_short = 50;
+
+ bss->send_probe_response = 1;
+
+#ifdef CONFIG_HS20
+ bss->hs20_release = (HS20_VERSION >> 4) + 1;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MACSEC
+ bss->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+ bss->macsec_port = 1;
+#endif /* CONFIG_MACSEC */
+
+ /* Default to strict CRL checking. */
+ bss->check_crl_strict = 1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ bss->sae_commit_status = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_PASN
+ /* comeback after 10 TUs */
+ bss->pasn_comeback_after = 10;
+ bss->pasn_noauth = 1;
+#endif /* CONFIG_PASN */
+}
+
+
+struct hostapd_config * hostapd_config_defaults(void)
+{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
+ struct hostapd_config *conf;
+ struct hostapd_bss_config *bss;
+ const int aCWmin = 4, aCWmax = 10;
+ const struct hostapd_wmm_ac_params ac_bk =
+ { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ const struct hostapd_wmm_ac_params ac_be =
+ { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+ { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
+ const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+ { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
+ const struct hostapd_tx_queue_params txq_bk =
+ { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_be =
+ { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
+ const struct hostapd_tx_queue_params txq_vi =
+ { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
+ const struct hostapd_tx_queue_params txq_vo =
+ { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
+
+#undef ecw2cw
+
+ conf = os_zalloc(sizeof(*conf));
+ bss = os_zalloc(sizeof(*bss));
+ if (conf == NULL || bss == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+ "configuration data.");
+ os_free(conf);
+ os_free(bss);
+ return NULL;
+ }
+ conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *));
+ if (conf->bss == NULL) {
+ os_free(conf);
+ os_free(bss);
+ return NULL;
+ }
+ conf->bss[0] = bss;
+
+ bss->radius = os_zalloc(sizeof(*bss->radius));
+ if (bss->radius == NULL) {
+ os_free(conf->bss);
+ os_free(conf);
+ os_free(bss);
+ return NULL;
+ }
+
+ hostapd_config_defaults_bss(bss);
+
+ conf->num_bss = 1;
+#ifdef CONFIG_24G_BW_SWITCH
+ /* yewei, add for enable 20M/40M switch, 20240118 */
+ conf->auto_20m_40m = 0;
+#endif /* CONFIG_24G_BW_SWITCH */
+#ifdef CONFIG_5G_BW_SWITCH
+ // To enable 20M/40M/80M bandwidth switch. Added by Liangyu Chu
+ conf->auto_80m = 0;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+ conf->beacon_int = 100;
+ conf->rts_threshold = -2; /* use driver default: 2347 */
+ conf->fragm_threshold = -2; /* user driver default: 2346 */
+ /* Set to invalid value means do not add Power Constraint IE */
+ conf->local_pwr_constraint = -1;
+#ifdef CONFIG_DFS_CAC_TIMER
+ /* Yewei, add dfs cac timer in seconds, 20240118 */
+ conf->dfs_cac_s = 0;
+#endif /* CONFIG_DFS_CAC_TIMER */
+ conf->wmm_ac_params[0] = ac_be;
+ conf->wmm_ac_params[1] = ac_bk;
+ conf->wmm_ac_params[2] = ac_vi;
+ conf->wmm_ac_params[3] = ac_vo;
+
+ conf->tx_queue[0] = txq_vo;
+ conf->tx_queue[1] = txq_vi;
+ conf->tx_queue[2] = txq_be;
+ conf->tx_queue[3] = txq_bk;
+
+ conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
+
+ conf->ap_table_max_size = 255;
+ conf->ap_table_expiration_time = 60;
+ conf->track_sta_max_age = 180;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ conf->ignore_probe_probability = 0.0;
+ conf->ignore_auth_probability = 0.0;
+ conf->ignore_assoc_probability = 0.0;
+ conf->ignore_reassoc_probability = 0.0;
+ conf->corrupt_gtk_rekey_mic_probability = 0.0;
+ conf->ecsa_ie_only = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ conf->acs = 0;
+ conf->acs_ch_list.num = 0;
+#ifdef CONFIG_ACS
+ conf->acs_num_scans = 5;
+#endif /* CONFIG_ACS */
+
+#ifdef CONFIG_IEEE80211AX
+ conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+ HE_OPERATION_RTS_THRESHOLD_OFFSET;
+ /* Set default basic MCS/NSS set to single stream MCS 0-7 */
+ conf->he_op.he_basic_mcs_nss_set = 0xfffc;
+ conf->he_op.he_bss_color_disabled = 1;
+ conf->he_op.he_bss_color_partial = 0;
+ conf->he_op.he_bss_color = os_random() % 63 + 1;
+ conf->he_op.he_twt_responder = 1;
+ conf->he_6ghz_max_mpdu = 2;
+ conf->he_6ghz_max_ampdu_len_exp = 7;
+ conf->he_6ghz_rx_ant_pat = 1;
+ conf->he_6ghz_tx_ant_pat = 1;
+#endif /* CONFIG_IEEE80211AX */
+
+ /* The third octet of the country string uses an ASCII space character
+ * by default to indicate that the regulations encompass all
+ * environments for the current frequency band in the country. */
+ conf->country[2] = ' ';
+
+ conf->rssi_reject_assoc_rssi = 0;
+ conf->rssi_reject_assoc_timeout = 30;
+
+#ifdef CONFIG_AIRTIME_POLICY
+ conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+ return conf;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b)
+{
+ return os_memcmp(a, b, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_wpa_psk(const char *fname,
+ struct hostapd_ssid *ssid)
+{
+ FILE *f;
+ char buf[128], *pos;
+ const char *keyid;
+ char *context;
+ char *context2;
+ char *token;
+ char *name;
+ char *value;
+ int line = 0, ret = 0, len, ok;
+ u8 addr[ETH_ALEN];
+ struct hostapd_wpa_psk *psk;
+
+ if (!fname)
+ return 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ int vlan_id = 0;
+ int wps = 0;
+
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ context = NULL;
+ keyid = NULL;
+ while ((token = str_token(buf, " ", &context))) {
+ if (!os_strchr(token, '='))
+ break;
+ context2 = NULL;
+ name = str_token(token, "=", &context2);
+ if (!name)
+ break;
+ value = str_token(token, "", &context2);
+ if (!value)
+ value = "";
+ if (!os_strcmp(name, "keyid")) {
+ keyid = value;
+ } else if (!os_strcmp(name, "wps")) {
+ wps = atoi(value);
+ } else if (!os_strcmp(name, "vlanid")) {
+ vlan_id = atoi(value);
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Unrecognized '%s=%s' on line %d in '%s'",
+ name, value, line, fname);
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret == -1)
+ break;
+
+ if (!token)
+ token = "";
+ if (hwaddr_aton(token, addr)) {
+ wpa_printf(MSG_ERROR,
+ "Invalid MAC address '%s' on line %d in '%s'",
+ token, line, fname);
+ ret = -1;
+ break;
+ }
+
+ psk = os_zalloc(sizeof(*psk));
+ if (psk == NULL) {
+ wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
+ ret = -1;
+ break;
+ }
+ psk->vlan_id = vlan_id;
+ if (is_zero_ether_addr(addr))
+ psk->group = 1;
+ else
+ os_memcpy(psk->addr, addr, ETH_ALEN);
+
+ pos = str_token(buf, "", &context);
+ if (!pos) {
+ wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
+ line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+
+ ok = 0;
+ len = os_strlen(pos);
+ if (len == 2 * PMK_LEN &&
+ hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
+ ok = 1;
+ else if (len >= 8 && len < 64 &&
+ pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
+ 4096, psk->psk, PMK_LEN) == 0)
+ ok = 1;
+ if (!ok) {
+ wpa_printf(MSG_ERROR,
+ "Invalid PSK '%s' on line %d in '%s'",
+ pos, line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+
+ if (keyid) {
+ len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid));
+ if ((size_t) len >= sizeof(psk->keyid)) {
+ wpa_printf(MSG_ERROR,
+ "PSK keyid too long on line %d in '%s'",
+ line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+ }
+
+ psk->wps = wps;
+
+ psk->next = ssid->wpa_psk;
+ ssid->wpa_psk = psk;
+ }
+
+ fclose(f);
+
+ return ret;
+}
+
+
+static int hostapd_derive_psk(struct hostapd_ssid *ssid)
+{
+ ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+ if (ssid->wpa_psk == NULL) {
+ wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "SSID",
+ (u8 *) ssid->ssid, ssid->ssid_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
+ (u8 *) ssid->wpa_passphrase,
+ os_strlen(ssid->wpa_passphrase));
+ if (pbkdf2_sha1(ssid->wpa_passphrase,
+ ssid->ssid, ssid->ssid_len,
+ 4096, ssid->wpa_psk->psk, PMK_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "Error in pbkdf2_sha1()");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
+ ssid->wpa_psk->psk, PMK_LEN);
+ return 0;
+}
+
+
+int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE
+ struct hostapd_ssid *ssid = &conf->ssid;
+ struct sae_password_entry *pw;
+
+ if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+ !hostapd_sae_pw_id_in_use(conf) &&
+ !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
+ !hostapd_sae_pk_in_use(conf)) ||
+ conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
+ !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
+ return 0; /* PT not needed */
+
+ sae_deinit_pt(ssid->pt);
+ ssid->pt = NULL;
+ if (ssid->wpa_passphrase) {
+ ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
+ ssid->ssid_len,
+ (const u8 *) ssid->wpa_passphrase,
+ os_strlen(ssid->wpa_passphrase),
+ NULL);
+ if (!ssid->pt)
+ return -1;
+ }
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ sae_deinit_pt(pw->pt);
+ pw->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
+ ssid->ssid_len,
+ (const u8 *) pw->password,
+ os_strlen(pw->password),
+ pw->identifier);
+ if (!pw->pt)
+ return -1;
+ }
+#endif /* CONFIG_SAE */
+
+ return 0;
+}
+
+
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
+{
+ struct hostapd_ssid *ssid = &conf->ssid;
+
+ if (hostapd_setup_sae_pt(conf) < 0)
+ return -1;
+
+ if (ssid->wpa_passphrase != NULL) {
+ if (ssid->wpa_psk != NULL) {
+ wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
+ "instead of passphrase");
+ } else {
+ wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
+ "passphrase");
+ if (hostapd_derive_psk(ssid) < 0)
+ return -1;
+ }
+ ssid->wpa_psk->group = 1;
+ }
+
+ return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid);
+}
+
+
+static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
+ int num_servers)
+{
+ int i;
+
+ for (i = 0; i < num_servers; i++) {
+ os_free(servers[i].shared_secret);
+ }
+ os_free(servers);
+}
+
+
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
+{
+ for (; attr; attr = attr->next) {
+ if (attr->type == type)
+ return attr;
+ }
+ return NULL;
+}
+
+
+struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value)
+{
+ const char *pos;
+ char syntax;
+ struct hostapd_radius_attr *attr;
+ size_t len;
+
+ attr = os_zalloc(sizeof(*attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = atoi(value);
+
+ pos = os_strchr(value, ':');
+ if (!pos) {
+ attr->val = wpabuf_alloc(1);
+ if (!attr->val) {
+ os_free(attr);
+ return NULL;
+ }
+ wpabuf_put_u8(attr->val, 0);
+ return attr;
+ }
+
+ pos++;
+ if (pos[0] == '\0' || pos[1] != ':') {
+ os_free(attr);
+ return NULL;
+ }
+ syntax = *pos++;
+ pos++;
+
+ switch (syntax) {
+ case 's':
+ attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
+ break;
+ case 'x':
+ len = os_strlen(pos);
+ if (len & 1)
+ break;
+ len /= 2;
+ attr->val = wpabuf_alloc(len);
+ if (!attr->val)
+ break;
+ if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
+ wpabuf_free(attr->val);
+ os_free(attr);
+ return NULL;
+ }
+ break;
+ case 'd':
+ attr->val = wpabuf_alloc(4);
+ if (attr->val)
+ wpabuf_put_be32(attr->val, atoi(pos));
+ break;
+ default:
+ os_free(attr);
+ return NULL;
+ }
+
+ if (!attr->val) {
+ os_free(attr);
+ return NULL;
+ }
+
+ return attr;
+}
+
+
+void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+{
+ struct hostapd_radius_attr *prev;
+
+ while (attr) {
+ prev = attr;
+ attr = attr->next;
+ wpabuf_free(prev->val);
+ os_free(prev);
+ }
+}
+
+
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+{
+ hostapd_config_free_radius_attr(user->accept_attr);
+ os_free(user->identity);
+ bin_clear_free(user->password, user->password_len);
+ bin_clear_free(user->salt, user->salt_len);
+ os_free(user);
+}
+
+
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user)
+{
+ struct hostapd_eap_user *prev_user;
+
+ while (user) {
+ prev_user = user;
+ user = user->next;
+ hostapd_config_free_eap_user(prev_user);
+ }
+}
+
+
+#ifdef CONFIG_WEP
+static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+{
+ int i;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ bin_clear_free(keys->key[i], keys->len[i]);
+ keys->key[i] = NULL;
+ }
+}
+#endif /* CONFIG_WEP */
+
+
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
+{
+ struct hostapd_wpa_psk *psk, *tmp;
+
+ for (psk = *l; psk;) {
+ tmp = psk;
+ psk = psk->next;
+ bin_clear_free(tmp, sizeof(*tmp));
+ }
+ *l = NULL;
+}
+
+
+static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
+{
+ struct anqp_element *elem;
+
+ while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
+ list))) {
+ dl_list_del(&elem->list);
+ wpabuf_free(elem->payload);
+ os_free(elem);
+ }
+}
+
+
+static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_FILS
+ struct fils_realm *realm;
+
+ while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm,
+ list))) {
+ dl_list_del(&realm->list);
+ os_free(realm);
+ }
+#endif /* CONFIG_FILS */
+}
+
+
+static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
+{
+ struct sae_password_entry *pw, *tmp;
+
+ pw = conf->sae_passwords;
+ conf->sae_passwords = NULL;
+ while (pw) {
+ tmp = pw;
+ pw = pw->next;
+ str_clear_free(tmp->password);
+ os_free(tmp->identifier);
+#ifdef CONFIG_SAE
+ sae_deinit_pt(tmp->pt);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_SAE_PK
+ sae_deinit_pk(tmp->pk);
+#endif /* CONFIG_SAE_PK */
+ os_free(tmp);
+ }
+}
+
+
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_controller_conf_free(struct dpp_controller_conf *conf)
+{
+ struct dpp_controller_conf *prev;
+
+ while (conf) {
+ prev = conf;
+ conf = conf->next;
+ os_free(prev);
+ }
+}
+#endif /* CONFIG_DPP2 */
+
+
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+{
+#if defined(CONFIG_WPS) || defined(CONFIG_HS20)
+ size_t i;
+#endif
+
+ if (conf == NULL)
+ return;
+
+ hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
+
+ str_clear_free(conf->ssid.wpa_passphrase);
+ os_free(conf->ssid.wpa_psk_file);
+#ifdef CONFIG_WEP
+ hostapd_config_free_wep(&conf->ssid.wep);
+#endif /* CONFIG_WEP */
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ os_free(conf->ssid.vlan_tagged_interface);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+#ifdef CONFIG_SAE
+ sae_deinit_pt(conf->ssid.pt);
+#endif /* CONFIG_SAE */
+
+ hostapd_config_free_eap_users(conf->eap_user);
+ os_free(conf->eap_user_sqlite);
+
+ os_free(conf->eap_req_id_text);
+ os_free(conf->erp_domain);
+ os_free(conf->accept_mac);
+ os_free(conf->deny_mac);
+ os_free(conf->nas_identifier);
+ if (conf->radius) {
+ hostapd_config_free_radius(conf->radius->auth_servers,
+ conf->radius->num_auth_servers);
+ hostapd_config_free_radius(conf->radius->acct_servers,
+ conf->radius->num_acct_servers);
+ os_free(conf->radius->force_client_dev);
+ }
+ hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
+ hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
+ os_free(conf->radius_req_attr_sqlite);
+ os_free(conf->rsn_preauth_interfaces);
+ os_free(conf->ctrl_interface);
+ os_free(conf->config_id);
+ os_free(conf->ca_cert);
+ os_free(conf->server_cert);
+ os_free(conf->server_cert2);
+ os_free(conf->private_key);
+ os_free(conf->private_key2);
+ os_free(conf->private_key_passwd);
+ os_free(conf->private_key_passwd2);
+ os_free(conf->check_cert_subject);
+ os_free(conf->ocsp_stapling_response);
+ os_free(conf->ocsp_stapling_response_multi);
+ os_free(conf->dh_file);
+ os_free(conf->openssl_ciphers);
+ os_free(conf->openssl_ecdh_curves);
+ os_free(conf->pac_opaque_encr_key);
+ os_free(conf->eap_fast_a_id);
+ os_free(conf->eap_fast_a_id_info);
+ os_free(conf->eap_sim_db);
+ os_free(conf->imsi_privacy_key);
+ os_free(conf->radius_server_clients);
+ os_free(conf->radius);
+ os_free(conf->radius_das_shared_secret);
+ hostapd_config_free_vlan(conf);
+ os_free(conf->time_zone);
+
+#ifdef CONFIG_IEEE80211R_AP
+ {
+ struct ft_remote_r0kh *r0kh, *r0kh_prev;
+ struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+ r0kh = conf->r0kh_list;
+ conf->r0kh_list = NULL;
+ while (r0kh) {
+ r0kh_prev = r0kh;
+ r0kh = r0kh->next;
+ os_free(r0kh_prev);
+ }
+
+ r1kh = conf->r1kh_list;
+ conf->r1kh_list = NULL;
+ while (r1kh) {
+ r1kh_prev = r1kh;
+ r1kh = r1kh->next;
+ os_free(r1kh_prev);
+ }
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_WPS
+ os_free(conf->wps_pin_requests);
+ os_free(conf->device_name);
+ os_free(conf->manufacturer);
+ os_free(conf->model_name);
+ os_free(conf->model_number);
+ os_free(conf->serial_number);
+ os_free(conf->config_methods);
+ os_free(conf->ap_pin);
+ os_free(conf->extra_cred);
+ os_free(conf->ap_settings);
+ hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
+ str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ os_free(conf->upnp_iface);
+ os_free(conf->friendly_name);
+ os_free(conf->manufacturer_url);
+ os_free(conf->model_description);
+ os_free(conf->model_url);
+ os_free(conf->upc);
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+ wpabuf_free(conf->wps_vendor_ext[i]);
+ wpabuf_free(conf->wps_application_ext);
+ wpabuf_free(conf->wps_nfc_dh_pubkey);
+ wpabuf_free(conf->wps_nfc_dh_privkey);
+ wpabuf_free(conf->wps_nfc_dev_pw);
+#endif /* CONFIG_WPS */
+
+ os_free(conf->roaming_consortium);
+ os_free(conf->venue_name);
+ os_free(conf->venue_url);
+ os_free(conf->nai_realm_data);
+ os_free(conf->network_auth_type);
+ os_free(conf->anqp_3gpp_cell_net);
+ os_free(conf->domain_name);
+ hostapd_config_free_anqp_elem(conf);
+
+#ifdef CONFIG_RADIUS_TEST
+ os_free(conf->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
+
+#ifdef CONFIG_HS20
+ os_free(conf->hs20_oper_friendly_name);
+ os_free(conf->hs20_wan_metrics);
+ os_free(conf->hs20_connection_capability);
+ os_free(conf->hs20_operating_class);
+ os_free(conf->hs20_icons);
+ if (conf->hs20_osu_providers) {
+ for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+ struct hs20_osu_provider *p;
+ size_t j;
+ p = &conf->hs20_osu_providers[i];
+ os_free(p->friendly_name);
+ os_free(p->server_uri);
+ os_free(p->method_list);
+ for (j = 0; j < p->icons_count; j++)
+ os_free(p->icons[j]);
+ os_free(p->icons);
+ os_free(p->osu_nai);
+ os_free(p->osu_nai2);
+ os_free(p->service_desc);
+ }
+ os_free(conf->hs20_osu_providers);
+ }
+ if (conf->hs20_operator_icon) {
+ for (i = 0; i < conf->hs20_operator_icon_count; i++)
+ os_free(conf->hs20_operator_icon[i]);
+ os_free(conf->hs20_operator_icon);
+ }
+ os_free(conf->subscr_remediation_url);
+ os_free(conf->hs20_sim_provisioning_url);
+ os_free(conf->t_c_filename);
+ os_free(conf->t_c_server_url);
+#endif /* CONFIG_HS20 */
+
+ wpabuf_free(conf->vendor_elements);
+ wpabuf_free(conf->assocresp_elements);
+
+ os_free(conf->sae_groups);
+#ifdef CONFIG_OWE
+ os_free(conf->owe_groups);
+#endif /* CONFIG_OWE */
+
+ os_free(conf->wowlan_triggers);
+
+ os_free(conf->server_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(conf->own_ie_override);
+ wpabuf_free(conf->sae_commit_override);
+ wpabuf_free(conf->rsne_override_eapol);
+ wpabuf_free(conf->rsnxe_override_eapol);
+ wpabuf_free(conf->rsne_override_ft);
+ wpabuf_free(conf->rsnxe_override_ft);
+ wpabuf_free(conf->gtk_rsc_override);
+ wpabuf_free(conf->igtk_rsc_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_free(conf->no_probe_resp_if_seen_on);
+ os_free(conf->no_auth_if_seen_on);
+
+ hostapd_config_free_fils_realms(conf);
+
+#ifdef CONFIG_DPP
+ os_free(conf->dpp_name);
+ os_free(conf->dpp_mud_url);
+ os_free(conf->dpp_extra_conf_req_name);
+ os_free(conf->dpp_extra_conf_req_value);
+ os_free(conf->dpp_connector);
+ wpabuf_free(conf->dpp_netaccesskey);
+ wpabuf_free(conf->dpp_csign);
+#ifdef CONFIG_DPP2
+ hostapd_dpp_controller_conf_free(conf->dpp_controller);
+#endif /* CONFIG_DPP2 */
+#endif /* CONFIG_DPP */
+
+ hostapd_config_free_sae_passwords(conf);
+
+#ifdef CONFIG_AIRTIME_POLICY
+ {
+ struct airtime_sta_weight *wt, *wt_prev;
+
+ wt = conf->airtime_weight_list;
+ conf->airtime_weight_list = NULL;
+ while (wt) {
+ wt_prev = wt;
+ wt = wt->next;
+ os_free(wt_prev);
+ }
+ }
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_PASN
+ os_free(conf->pasn_groups);
+#endif /* CONFIG_PASN */
+
+ os_free(conf);
+}
+
+
+/**
+ * hostapd_config_free - Free hostapd configuration
+ * @conf: Configuration data from hostapd_config_read().
+ */
+void hostapd_config_free(struct hostapd_config *conf)
+{
+ size_t i;
+
+ if (conf == NULL)
+ return;
+
+ for (i = 0; i < conf->num_bss; i++)
+ hostapd_config_free_bss(conf->bss[i]);
+ os_free(conf->config_id);
+ os_free(conf->bss);
+ os_free(conf->supported_rates);
+ os_free(conf->basic_rates);
+ os_free(conf->acs_ch_list.range);
+ os_free(conf->acs_freq_list.range);
+ os_free(conf->driver_params);
+#ifdef CONFIG_ACS
+ os_free(conf->acs_chan_bias);
+#endif /* CONFIG_ACS */
+ wpabuf_free(conf->lci);
+ wpabuf_free(conf->civic);
+
+ os_free(conf);
+}
+
+
+/**
+ * hostapd_maclist_found - Find a MAC address from a list
+ * @list: MAC address list
+ * @num_entries: Number of addresses in the list
+ * @addr: Address to search for
+ * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
+ * Returns: 1 if address is in the list or 0 if not.
+ *
+ * Perform a binary search for given MAC address from a pre-sorted list.
+ */
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+ const u8 *addr, struct vlan_description *vlan_id)
+{
+ int start, end, middle, res;
+
+ start = 0;
+ end = num_entries - 1;
+
+ while (start <= end) {
+ middle = (start + end) / 2;
+ res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
+ if (res == 0) {
+ if (vlan_id)
+ *vlan_id = list[middle].vlan_id;
+ return 1;
+ }
+ if (res < 0)
+ start = middle + 1;
+ else
+ end = middle - 1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_rate_found(int *list, int rate)
+{
+ int i;
+
+ if (list == NULL)
+ return 0;
+
+ for (i = 0; list[i] >= 0; i++)
+ if (list[i] == rate)
+ return 1;
+
+ return 0;
+}
+
+
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+ struct vlan_description *vlan_desc)
+{
+ struct hostapd_vlan *v = vlan;
+ int i;
+
+ if (!vlan_desc->notempty || vlan_desc->untagged < 0 ||
+ vlan_desc->untagged > MAX_VLAN_ID)
+ return 0;
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
+ if (vlan_desc->tagged[i] < 0 ||
+ vlan_desc->tagged[i] > MAX_VLAN_ID)
+ return 0;
+ }
+ if (!vlan_desc->untagged && !vlan_desc->tagged[0])
+ return 0;
+
+ while (v) {
+ if (!vlan_compare(&v->vlan_desc, vlan_desc) ||
+ v->vlan_id == VLAN_ID_WILDCARD)
+ return 1;
+ v = v->next;
+ }
+ return 0;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+ struct hostapd_vlan *v = vlan;
+ while (v) {
+ if (v->vlan_id == vlan_id)
+ return v->ifname;
+ v = v->next;
+ }
+ return NULL;
+}
+
+
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk, int *vlan_id)
+{
+ struct hostapd_wpa_psk *psk;
+ int next_ok = prev_psk == NULL;
+
+ if (vlan_id)
+ *vlan_id = 0;
+
+ if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " p2p_dev_addr=" MACSTR " prev_psk=%p",
+ MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
+ addr = NULL; /* Use P2P Device Address for matching */
+ } else {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " prev_psk=%p",
+ MAC2STR(addr), prev_psk);
+ }
+
+ for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
+ if (next_ok &&
+ (psk->group ||
+ (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (!addr && p2p_dev_addr &&
+ os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+ 0))) {
+ if (vlan_id)
+ *vlan_id = psk->vlan_id;
+ return psk->psk;
+ }
+
+ if (psk->psk == prev_psk)
+ next_ok = 1;
+ }
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_SAE_PK
+static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
+{
+ struct sae_password_entry *pw;
+ bool res = false;
+
+ if (bss->ssid.wpa_passphrase &&
+#ifdef CONFIG_TESTING_OPTIONS
+ !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+ sae_pk_valid_password(bss->ssid.wpa_passphrase))
+ res = true;
+
+ for (pw = bss->sae_passwords; pw; pw = pw->next) {
+ if (!pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+ !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+ sae_pk_valid_password(pw->password))
+ return true;
+
+ if (bss->ssid.wpa_passphrase && res && pw->pk &&
+ os_strcmp(bss->ssid.wpa_passphrase, pw->password) == 0)
+ res = false;
+ }
+
+ return res;
+}
+#endif /* CONFIG_SAE_PK */
+
+
+static bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
+{
+ if (bss->wpa != WPA_PROTO_RSN) {
+ wpa_printf(MSG_ERROR,
+ "Pre-RSNA security methods are not allowed in 6 GHz");
+ return false;
+ }
+
+ if (bss->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_printf(MSG_ERROR,
+ "Management frame protection is required in 6 GHz");
+ return false;
+ }
+
+ if (bss->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256)) {
+ wpa_printf(MSG_ERROR, "Invalid AKM suite for 6 GHz");
+ return false;
+ }
+
+ if (bss->rsn_pairwise & (WPA_CIPHER_WEP40 |
+ WPA_CIPHER_WEP104 |
+ WPA_CIPHER_TKIP)) {
+ wpa_printf(MSG_ERROR,
+ "Invalid pairwise cipher suite for 6 GHz");
+ return false;
+ }
+
+ if (bss->wpa_group & (WPA_CIPHER_WEP40 |
+ WPA_CIPHER_WEP104 |
+ WPA_CIPHER_TKIP)) {
+ wpa_printf(MSG_ERROR, "Invalid group cipher suite for 6 GHz");
+ return false;
+ }
+
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+ bss->sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+ wpa_printf(MSG_INFO, "SAE: Enabling SAE H2E on 6 GHz");
+ bss->sae_pwe = SAE_PWE_BOTH;
+ }
+#endif /* CONFIG_SAE */
+
+ return true;
+}
+
+
+static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
+ struct hostapd_config *conf,
+ int full_config)
+{
+ if (full_config && is_6ghz_op_class(conf->op_class) &&
+ !hostapd_config_check_bss_6g(bss))
+ return -1;
+
+ if (full_config && bss->ieee802_1x && !bss->eap_server &&
+ !bss->radius->auth_servers) {
+ wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
+ "EAP authenticator configured).");
+ return -1;
+ }
+
+#ifdef CONFIG_WEP
+ if (bss->wpa) {
+ int wep, i;
+
+ wep = bss->default_wep_key_len > 0 ||
+ bss->individual_wep_key_len > 0;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (bss->ssid.wep.keys_set) {
+ wep = 1;
+ break;
+ }
+ }
+
+ if (wep) {
+ wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
+ return -1;
+ }
+ }
+#endif /* CONFIG_WEP */
+
+ if (full_config && bss->wpa &&
+ bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
+ "RADIUS checking (macaddr_acl=2) enabled.");
+ return -1;
+ }
+
+ if (full_config && bss->wpa &&
+ wpa_key_mgmt_wpa_psk_no_sae(bss->wpa_key_mgmt) &&
+ bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
+ bss->ssid.wpa_psk_file == NULL &&
+ bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
+ (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
+ wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
+ "is not configured.");
+ return -1;
+ }
+
+ if (full_config && !is_zero_ether_addr(bss->bssid)) {
+ size_t i;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (conf->bss[i] != bss &&
+ (hostapd_mac_comp(conf->bss[i]->bssid,
+ bss->bssid) == 0)) {
+ wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
+ " on interface '%s' and '%s'.",
+ MAC2STR(bss->bssid),
+ conf->bss[i]->iface, bss->iface);
+ return -1;
+ }
+ }
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
+ (bss->nas_identifier == NULL ||
+ os_strlen(bss->nas_identifier) < 1 ||
+ os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
+ wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
+ "nas_identifier to be configured as a 1..48 octet "
+ "string");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if (full_config && conf->ieee80211n &&
+ conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+ bss->disable_11n = true;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+ "allowed, disabling HT capabilities");
+ }
+
+#ifdef CONFIG_WEP
+ if (full_config && conf->ieee80211n &&
+ bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+ bss->disable_11n = true;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
+ "allowed, disabling HT capabilities");
+ }
+#endif /* CONFIG_WEP */
+
+ if (full_config && conf->ieee80211n && bss->wpa &&
+ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+ {
+ bss->disable_11n = true;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
+ "requires CCMP/GCMP to be enabled, disabling HT "
+ "capabilities");
+ }
+
+#ifdef CONFIG_IEEE80211AC
+#ifdef CONFIG_WEP
+ if (full_config && conf->ieee80211ac &&
+ bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+ bss->disable_11ac = true;
+ wpa_printf(MSG_ERROR,
+ "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
+ }
+#endif /* CONFIG_WEP */
+
+ if (full_config && conf->ieee80211ac && bss->wpa &&
+ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+ {
+ bss->disable_11ac = true;
+ wpa_printf(MSG_ERROR,
+ "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+#ifdef CONFIG_WEP
+ if (full_config && conf->ieee80211ax &&
+ bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+ bss->disable_11ax = true;
+ wpa_printf(MSG_ERROR,
+ "HE (IEEE 802.11ax) with WEP is not allowed, disabling HE capabilities");
+ }
+#endif /* CONFIG_WEP */
+
+ if (full_config && conf->ieee80211ax && bss->wpa &&
+ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+ {
+ bss->disable_11ax = true;
+ wpa_printf(MSG_ERROR,
+ "HE (IEEE 802.11ax) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling HE capabilities");
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_WPS
+ if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
+ wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+ "configuration forced WPS to be disabled");
+ bss->wps_state = 0;
+ }
+
+#ifdef CONFIG_WEP
+ if (full_config && bss->wps_state &&
+ bss->ssid.wep.keys_set && bss->wpa == 0) {
+ wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
+ "disabled");
+ bss->wps_state = 0;
+ }
+#endif /* CONFIG_WEP */
+
+ if (full_config && bss->wps_state && bss->wpa &&
+ (!(bss->wpa & 2) ||
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)))) {
+ wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+ "WPA2/CCMP/GCMP forced WPS to be disabled");
+ bss->wps_state = 0;
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+ if (full_config && bss->hs20 &&
+ (!(bss->wpa & 2) ||
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)))) {
+ wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+ "configuration is required for Hotspot 2.0 "
+ "functionality");
+ return -1;
+ }
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MBO
+ if (full_config && bss->mbo_enabled && (bss->wpa & 2) &&
+ bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ wpa_printf(MSG_ERROR,
+ "MBO: PMF needs to be enabled whenever using WPA2 with MBO");
+ return -1;
+ }
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_OCV
+ if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
+ bss->ocv) {
+ wpa_printf(MSG_ERROR,
+ "OCV: PMF needs to be enabled whenever using OCV");
+ return -1;
+ }
+#endif /* CONFIG_OCV */
+
+#ifdef CONFIG_SAE_PK
+ if (full_config && hostapd_sae_pk_in_use(bss) &&
+ hostapd_sae_pk_password_without_pk(bss)) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: SAE password uses SAE-PK style, but does not have PK configured");
+ return -1;
+ }
+#endif /* CONFIG_SAE_PK */
+
+#ifdef CONFIG_FILS
+ if (full_config && bss->fils_discovery_max_int &&
+ (!conf->ieee80211ax || bss->disable_11ax)) {
+ wpa_printf(MSG_ERROR,
+ "Currently IEEE 802.11ax support is mandatory to enable FILS discovery transmission.");
+ return -1;
+ }
+
+ if (full_config && bss->fils_discovery_max_int &&
+ bss->unsol_bcast_probe_resp_interval) {
+ wpa_printf(MSG_ERROR,
+ "Cannot enable both FILS discovery and unsolicited broadcast Probe Response at the same time");
+ return -1;
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211BE
+ if (full_config && !bss->disable_11be && bss->disable_11ax) {
+ bss->disable_11be = true;
+ wpa_printf(MSG_INFO,
+ "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
+ wpa_printf(MSG_ERROR,
+ "Hidden SSID is not suppored when MBSSID is enabled");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
+{
+ int tx_cwmin = conf->tx_queue[queue].cwmin;
+ int tx_cwmax = conf->tx_queue[queue].cwmax;
+ int ac_cwmin = conf->wmm_ac_params[queue].cwmin;
+ int ac_cwmax = conf->wmm_ac_params[queue].cwmax;
+
+ if (tx_cwmin > tx_cwmax) {
+ wpa_printf(MSG_ERROR,
+ "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+ tx_cwmin, tx_cwmax);
+ return -1;
+ }
+ if (ac_cwmin > ac_cwmax) {
+ wpa_printf(MSG_ERROR,
+ "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+ ac_cwmin, ac_cwmax);
+ return -1;
+ }
+ return 0;
+}
+
+
+int hostapd_config_check(struct hostapd_config *conf, int full_config)
+{
+ size_t i;
+
+ if (full_config && is_6ghz_op_class(conf->op_class) &&
+ !conf->hw_mode_set) {
+ /* Use the appropriate hw_mode value automatically when the
+ * op_class parameter has been set, but hw_mode was not. */
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+ }
+
+ if (full_config && conf->ieee80211d &&
+ (!conf->country[0] || !conf->country[1])) {
+ wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
+ "setting the country_code");
+ return -1;
+ }
+
+ if (full_config && conf->ieee80211h && !conf->ieee80211d) {
+ wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
+ "IEEE 802.11d enabled");
+ return -1;
+ }
+
+ if (full_config && conf->local_pwr_constraint != -1 &&
+ !conf->ieee80211d) {
+ wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
+ return -1;
+ }
+
+ if (full_config && conf->spectrum_mgmt_required &&
+ conf->local_pwr_constraint == -1) {
+ wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
+ return -1;
+ }
+
+#ifdef CONFIG_AIRTIME_POLICY
+ if (full_config && conf->airtime_mode > AIRTIME_MODE_STATIC &&
+ !conf->airtime_update_interval) {
+ wpa_printf(MSG_ERROR, "Airtime update interval cannot be zero");
+ return -1;
+ }
+#endif /* CONFIG_AIRTIME_POLICY */
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ if (hostapd_config_check_cw(conf, i))
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (full_config && conf->ieee80211be && !conf->ieee80211ax) {
+ wpa_printf(MSG_ERROR,
+ "Cannot set ieee80211be without ieee80211ax");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (full_config && conf->mbssid && !conf->ieee80211ax) {
+ wpa_printf(MSG_ERROR,
+ "Cannot enable multiple BSSID support without ieee80211ax");
+ return -1;
+ }
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+ int full_config)
+{
+#ifdef CONFIG_WEP
+ if (bss->individual_wep_key_len == 0) {
+ /* individual keys are not use; can use key idx0 for
+ * broadcast keys */
+ bss->broadcast_key_idx_min = 0;
+ }
+#endif /* CONFIG_WEP */
+
+ if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+ bss->rsn_pairwise = bss->wpa_pairwise;
+ if (bss->group_cipher)
+ bss->wpa_group = bss->group_cipher;
+ else
+ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+ bss->wpa_pairwise,
+ bss->rsn_pairwise);
+ if (!bss->wpa_group_rekey_set)
+ bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
+ 600 : 86400;
+
+ if (full_config) {
+ bss->radius->auth_server = bss->radius->auth_servers;
+ bss->radius->acct_server = bss->radius->acct_servers;
+ }
+
+ if (bss->wpa && bss->ieee802_1x) {
+ bss->ssid.security_policy = SECURITY_WPA;
+ } else if (bss->wpa) {
+ bss->ssid.security_policy = SECURITY_WPA_PSK;
+ } else if (bss->ieee802_1x) {
+ int cipher = WPA_CIPHER_NONE;
+ bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+#ifdef CONFIG_WEP
+ bss->ssid.wep.default_len = bss->default_wep_key_len;
+ if (full_config && bss->default_wep_key_len) {
+ cipher = bss->default_wep_key_len >= 13 ?
+ WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+ } else if (full_config && bss->ssid.wep.keys_set) {
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
+ else
+ cipher = WPA_CIPHER_WEP40;
+ }
+#endif /* CONFIG_WEP */
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ if (full_config)
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+#ifdef CONFIG_WEP
+ } else if (bss->ssid.wep.keys_set) {
+ int cipher = WPA_CIPHER_WEP40;
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
+ bss->ssid.security_policy = SECURITY_STATIC_WEP;
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ if (full_config)
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+#endif /* CONFIG_WEP */
+ } else if (bss->osen) {
+ bss->ssid.security_policy = SECURITY_OSEN;
+ bss->wpa_group = WPA_CIPHER_CCMP;
+ bss->wpa_pairwise = 0;
+ bss->rsn_pairwise = WPA_CIPHER_CCMP;
+ } else {
+ bss->ssid.security_policy = SECURITY_PLAINTEXT;
+ if (full_config) {
+ bss->wpa_group = WPA_CIPHER_NONE;
+ bss->wpa_pairwise = WPA_CIPHER_NONE;
+ bss->rsn_pairwise = WPA_CIPHER_NONE;
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+ }
+ }
+}
+
+
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
+{
+ int with_id = 0, without_id = 0;
+ struct sae_password_entry *pw;
+
+ if (conf->ssid.wpa_passphrase)
+ without_id = 1;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->identifier)
+ with_id = 1;
+ else
+ without_id = 1;
+ if (with_id && without_id)
+ break;
+ }
+
+ if (with_id && !without_id)
+ return 2;
+ return with_id;
+}
+
+
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE_PK
+ struct sae_password_entry *pw;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->pk)
+ return true;
+ }
+#endif /* CONFIG_SAE_PK */
+
+ return false;
+}
+
+
+#ifdef CONFIG_SAE_PK
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
+{
+ bool with_pk = false;
+ struct sae_password_entry *pw;
+
+ if (conf->ssid.wpa_passphrase)
+ return false;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (!pw->pk)
+ return false;
+ with_pk = true;
+ }
+
+ return with_pk;
+}
+#endif /* CONFIG_SAE_PK */
+
+
+int hostapd_acl_comp(const void *a, const void *b)
+{
+ const struct mac_acl_entry *aa = a;
+ const struct mac_acl_entry *bb = b;
+ return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
+}
+
+
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+ int vlan_id, const u8 *addr)
+{
+ struct mac_acl_entry *newacl;
+
+ newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+ if (!newacl) {
+ wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+ return -1;
+ }
+
+ *acl = newacl;
+ os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+ os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+ (*acl)[*num].vlan_id.untagged = vlan_id;
+ (*acl)[*num].vlan_id.notempty = !!vlan_id;
+ (*num)++;
+
+ return 0;
+}
+
+
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+ const u8 *addr)
+{
+ int i = 0;
+
+ while (i < *num) {
+ if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+ os_remove_in_array(*acl, *num, sizeof(**acl), i);
+ (*num)--;
+ } else {
+ i++;
+ }
+ }
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.h
new file mode 100644
index 0000000..dc95316
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.h
@@ -0,0 +1,1329 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "vlan.h"
+
+enum macaddr_acl {
+ ACCEPT_UNLESS_DENIED = 0,
+ DENY_UNLESS_ACCEPTED = 1,
+ USE_EXTERNAL_RADIUS_AUTH = 2
+};
+
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+ u8 meshid[32];
+ u8 meshid_len;
+ /* Active Path Selection Protocol Identifier */
+ u8 mesh_pp_id;
+ /* Active Path Selection Metric Identifier */
+ u8 mesh_pm_id;
+ /* Congestion Control Mode Identifier */
+ u8 mesh_cc_id;
+ /* Synchronization Protocol Identifier */
+ u8 mesh_sp_id;
+ /* Authentication Protocol Identifier */
+ u8 mesh_auth_id;
+ u8 *rsn_ie;
+ int rsn_ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+ unsigned int security;
+ enum mfp_options ieee80211w;
+ int ocv;
+ unsigned int pairwise_cipher;
+ unsigned int group_cipher;
+ unsigned int mgmt_group_cipher;
+ int dot11MeshMaxRetries;
+ int dot11MeshRetryTimeout; /* msec */
+ int dot11MeshConfirmTimeout; /* msec */
+ int dot11MeshHoldingTimeout; /* msec */
+ int mesh_fwding;
+};
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct mac_acl_entry {
+ macaddr addr;
+ struct vlan_description vlan_id;
+};
+
+struct hostapd_radius_servers;
+struct ft_remote_r0kh;
+struct ft_remote_r1kh;
+
+#ifdef CONFIG_WEP
+#define NUM_WEP_KEYS 4
+struct hostapd_wep_keys {
+ u8 idx;
+ u8 *key[NUM_WEP_KEYS];
+ size_t len[NUM_WEP_KEYS];
+ int keys_set;
+ size_t default_len; /* key length used for dynamic key generation */
+};
+#endif /* CONFIG_WEP */
+
+typedef enum hostap_security_policy {
+ SECURITY_PLAINTEXT = 0,
+#ifdef CONFIG_WEP
+ SECURITY_STATIC_WEP = 1,
+#endif /* CONFIG_WEP */
+ SECURITY_IEEE_802_1X = 2,
+ SECURITY_WPA_PSK = 3,
+ SECURITY_WPA = 4,
+ SECURITY_OSEN = 5
+} secpolicy;
+
+struct hostapd_ssid {
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ u32 short_ssid;
+ unsigned int ssid_set:1;
+ unsigned int utf8_ssid:1;
+ unsigned int wpa_passphrase_set:1;
+ unsigned int wpa_psk_set:1;
+
+ char vlan[IFNAMSIZ + 1];
+ secpolicy security_policy;
+
+ struct hostapd_wpa_psk *wpa_psk;
+ char *wpa_passphrase;
+ char *wpa_psk_file;
+ struct sae_pt *pt;
+
+#ifdef CONFIG_WEP
+ struct hostapd_wep_keys wep;
+#endif /* CONFIG_WEP */
+
+#define DYNAMIC_VLAN_DISABLED 0
+#define DYNAMIC_VLAN_OPTIONAL 1
+#define DYNAMIC_VLAN_REQUIRED 2
+ int dynamic_vlan;
+ int vlan_no_bridge;
+#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+#define DYNAMIC_VLAN_NAMING_END 2
+ int vlan_naming;
+ int per_sta_vif;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+ struct hostapd_vlan *next;
+ int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+ struct vlan_description vlan_desc;
+ char ifname[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
+ int configured;
+ int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_WLAN_PORT 0x8
+ int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+#define PMK_LEN 32
+#define KEYID_LEN 32
+#define MIN_PASSPHRASE_LEN 8
+#define MAX_PASSPHRASE_LEN 63
+struct hostapd_sta_wpa_psk_short {
+ struct hostapd_sta_wpa_psk_short *next;
+ unsigned int is_passphrase:1;
+ u8 psk[PMK_LEN];
+ char passphrase[MAX_PASSPHRASE_LEN + 1];
+ int ref; /* (number of references held) - 1 */
+};
+
+struct hostapd_wpa_psk {
+ struct hostapd_wpa_psk *next;
+ int group;
+ char keyid[KEYID_LEN];
+ int wps;
+ u8 psk[PMK_LEN];
+ u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
+ int vlan_id;
+};
+
+struct hostapd_eap_user {
+ struct hostapd_eap_user *next;
+ u8 *identity;
+ size_t identity_len;
+ struct {
+ int vendor;
+ u32 method;
+ } methods[EAP_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
+ u8 *salt;
+ size_t salt_len; /* non-zero when password is salted */
+ int phase2;
+ int force_version;
+ unsigned int wildcard_prefix:1;
+ unsigned int password_hash:1; /* whether password is hashed with
+ * nt_password_hash() */
+ unsigned int remediation:1;
+ unsigned int macacl:1;
+ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+ struct hostapd_radius_attr *accept_attr;
+ u32 t_c_timestamp;
+};
+
+struct hostapd_radius_attr {
+ u8 type;
+ struct wpabuf *val;
+ struct hostapd_radius_attr *next;
+};
+
+
+#define NUM_TX_QUEUES 4
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+ u8 len;
+ u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
+struct hostapd_lang_string {
+ u8 lang[3];
+ u8 name_len;
+ u8 name[252];
+};
+
+struct hostapd_venue_url {
+ u8 venue_number;
+ u8 url_len;
+ u8 url[254];
+};
+
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+ u8 encoding;
+ char realm_buf[MAX_NAI_REALMLEN + 1];
+ char *realm[MAX_NAI_REALMS];
+ u8 eap_method_count;
+ struct hostapd_nai_realm_eap {
+ u8 eap_method;
+ u8 num_auths;
+ u8 auth_id[MAX_NAI_AUTH_TYPES];
+ u8 auth_val[MAX_NAI_AUTH_TYPES];
+ } eap_method[MAX_NAI_EAP_METHODS];
+};
+
+struct anqp_element {
+ struct dl_list list;
+ u16 infoid;
+ struct wpabuf *payload;
+};
+
+struct fils_realm {
+ struct dl_list list;
+ u8 hash[2];
+ char realm[];
+};
+
+struct sae_password_entry {
+ struct sae_password_entry *next;
+ char *password;
+ char *identifier;
+ u8 peer_addr[ETH_ALEN];
+ int vlan_id;
+ struct sae_pt *pt;
+ struct sae_pk *pk;
+};
+
+struct dpp_controller_conf {
+ struct dpp_controller_conf *next;
+ u8 pkhash[SHA256_MAC_LEN];
+ struct hostapd_ip_addr ipaddr;
+};
+
+struct airtime_sta_weight {
+ struct airtime_sta_weight *next;
+ unsigned int weight;
+ u8 addr[ETH_ALEN];
+};
+
+#define EXT_CAPA_MAX_LEN 15
+
+/**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+struct hostapd_bss_config {
+ char iface[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
+ char ft_iface[IFNAMSIZ + 1];
+ char snoop_iface[IFNAMSIZ + 1];
+ char vlan_bridge[IFNAMSIZ + 1];
+ char wds_bridge[IFNAMSIZ + 1];
+ int bridge_hairpin; /* hairpin_mode on bridge members */
+
+ enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
+
+ unsigned int logger_syslog; /* module bitfield */
+ unsigned int logger_stdout; /* module bitfield */
+
+ int max_num_sta; /* maximum number of STAs in station table */
+
+ int dtim_period;
+ unsigned int bss_load_update_period;
+ unsigned int chan_util_avg_period;
+
+ int ieee802_1x; /* use IEEE 802.1X */
+ int eapol_version;
+ int eap_server; /* Use internal EAP server instead of external
+ * RADIUS server */
+ struct hostapd_eap_user *eap_user;
+ char *eap_user_sqlite;
+ char *eap_sim_db;
+ unsigned int eap_sim_db_timeout;
+ int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+ struct hostapd_ip_addr own_ip_addr;
+ int dynamic_own_ip_addr;
+ char *nas_identifier;
+ struct hostapd_radius_servers *radius;
+ int acct_interim_interval;
+ int radius_request_cui;
+ struct hostapd_radius_attr *radius_auth_req_attr;
+ struct hostapd_radius_attr *radius_acct_req_attr;
+ char *radius_req_attr_sqlite;
+ int radius_das_port;
+ unsigned int radius_das_time_window;
+ int radius_das_require_event_timestamp;
+ int radius_das_require_message_authenticator;
+ struct hostapd_ip_addr radius_das_client_addr;
+ u8 *radius_das_shared_secret;
+ size_t radius_das_shared_secret_len;
+
+ struct hostapd_ssid ssid;
+
+ char *eap_req_id_text; /* optional displayable message sent with
+ * EAP Request-Identity */
+ size_t eap_req_id_text_len;
+ int eapol_key_index_workaround;
+
+#ifdef CONFIG_WEP
+ size_t default_wep_key_len;
+ int individual_wep_key_len;
+ int wep_rekeying_period;
+ int broadcast_key_idx_min, broadcast_key_idx_max;
+#endif /* CONFIG_WEP */
+ int eap_reauth_period;
+ int erp_send_reauth_start;
+ char *erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+ bool eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ enum macaddr_acl macaddr_acl;
+ struct mac_acl_entry *accept_mac;
+ int num_accept_mac;
+ struct mac_acl_entry *deny_mac;
+ int num_deny_mac;
+ int wds_sta;
+ int isolate;
+ int start_disabled;
+
+ int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+ * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
+
+ int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+ int extended_key_id;
+ int wpa_key_mgmt;
+ enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
+ int beacon_prot;
+ /* dot11AssociationSAQueryMaximumTimeout (in TUs) */
+ unsigned int assoc_sa_query_max_timeout;
+ /* dot11AssociationSAQueryRetryTimeout (in TUs) */
+ int assoc_sa_query_retry_timeout;
+#ifdef CONFIG_OCV
+ int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
+ enum {
+ PSK_RADIUS_IGNORED = 0,
+ PSK_RADIUS_ACCEPTED = 1,
+ PSK_RADIUS_REQUIRED = 2,
+ PSK_RADIUS_DURING_4WAY_HS = 3,
+ } wpa_psk_radius;
+ int wpa_pairwise;
+ int group_cipher; /* wpa_group value override from configuation */
+ int wpa_group;
+ int wpa_group_rekey;
+ int wpa_group_rekey_set;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
+ enum ptk0_rekey_handling wpa_deny_ptk0_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
+ int wpa_disable_eapol_key_retries;
+ int rsn_pairwise;
+ int rsn_preauth;
+ char *rsn_preauth_interfaces;
+
+#ifdef CONFIG_IEEE80211R_AP
+ /* IEEE 802.11r - Fast BSS Transition */
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r1_key_holder[FT_R1KH_ID_LEN];
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+ int rkh_pos_timeout;
+ int rkh_neg_timeout;
+ int rkh_pull_timeout; /* ms */
+ int rkh_pull_retries;
+ u32 reassociation_deadline;
+ struct ft_remote_r0kh *r0kh_list;
+ struct ft_remote_r1kh *r1kh_list;
+ int pmk_r1_push;
+ int ft_over_ds;
+ int ft_psk_generate_local;
+ int r1_max_key_lifetime;
+#endif /* CONFIG_IEEE80211R_AP */
+
+ char *ctrl_interface; /* directory for UNIX domain sockets */
+#ifndef CONFIG_NATIVE_WINDOWS
+ gid_t ctrl_interface_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+ int ctrl_interface_gid_set;
+
+ char *ca_cert;
+ char *server_cert;
+ char *server_cert2;
+ char *private_key;
+ char *private_key2;
+ char *private_key_passwd;
+ char *private_key_passwd2;
+ char *check_cert_subject;
+ int check_crl;
+ int check_crl_strict;
+ unsigned int crl_reload_interval;
+ unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
+ unsigned int max_auth_rounds;
+ unsigned int max_auth_rounds_short;
+ char *ocsp_stapling_response;
+ char *ocsp_stapling_response_multi;
+ char *dh_file;
+ char *openssl_ciphers;
+ char *openssl_ecdh_curves;
+ u8 *pac_opaque_encr_key;
+ u8 *eap_fast_a_id;
+ size_t eap_fast_a_id_len;
+ char *eap_fast_a_id_info;
+ int eap_fast_prov;
+ int pac_key_lifetime;
+ int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
+ int eap_teap_separate_result;
+ int eap_teap_id;
+ int eap_teap_method_sequence;
+ int eap_sim_aka_result_ind;
+ int eap_sim_id;
+ char *imsi_privacy_key;
+ int tnc;
+ int fragment_size;
+ u16 pwd_group;
+
+ char *radius_server_clients;
+ int radius_server_auth_port;
+ int radius_server_acct_port;
+ int radius_server_ipv6;
+
+ int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
+ * address instead of individual address
+ * (for driver_wired.c).
+ */
+
+ int ap_max_inactivity;
+ int ignore_broadcast_ssid;
+ int no_probe_resp_if_max_sta;
+
+ int wmm_enabled;
+ int wmm_uapsd;
+
+ struct hostapd_vlan *vlan;
+
+ macaddr bssid;
+
+ /*
+ * Maximum listen interval that STAs can use when associating with this
+ * BSS. If a STA tries to use larger value, the association will be
+ * denied with status code 51.
+ */
+ u16 max_listen_interval;
+
+ int disable_pmksa_caching;
+ int okc; /* Opportunistic Key Caching */
+
+ int wps_state;
+#ifdef CONFIG_WPS
+ int wps_independent;
+ int ap_setup_locked;
+ u8 uuid[16];
+ char *wps_pin_requests;
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ u8 device_type[WPS_DEV_TYPE_LEN];
+ char *config_methods;
+ u8 os_version[4];
+ char *ap_pin;
+ int skip_cred_build;
+ u8 *extra_cred;
+ size_t extra_cred_len;
+ int wps_cred_processing;
+ int wps_cred_add_sae;
+ int force_per_enrollee_psk;
+ u8 *ap_settings;
+ size_t ap_settings_len;
+ struct hostapd_ssid multi_ap_backhaul_ssid;
+ char *upnp_iface;
+ char *friendly_name;
+ char *manufacturer_url;
+ char *model_description;
+ char *model_url;
+ char *upc;
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ struct wpabuf *wps_application_ext;
+ int wps_nfc_pw_from_config;
+ int wps_nfc_dev_pw_id;
+ struct wpabuf *wps_nfc_dh_pubkey;
+ struct wpabuf *wps_nfc_dh_privkey;
+ struct wpabuf *wps_nfc_dev_pw;
+#endif /* CONFIG_WPS */
+ int pbc_in_m1;
+ char *server_id;
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#define P2P_MANAGE BIT(3)
+#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
+ int p2p;
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
+
+ int disassoc_low_ack;
+ int skip_inactivity_poll;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+ int tdls;
+ bool disable_11n;
+ bool disable_11ac;
+ bool disable_11ax;
+ bool disable_11be;
+
+ /* IEEE 802.11v */
+ int time_advertisement;
+ char *time_zone;
+ int wnm_sleep_mode;
+ int wnm_sleep_mode_no_keys;
+ int bss_transition;
+
+ /* IEEE 802.11u - Interworking */
+ int interworking;
+ int access_network_type;
+ int internet;
+ int asra;
+ int esr;
+ int uesa;
+ int venue_info_set;
+ u8 venue_group;
+ u8 venue_type;
+ u8 hessid[ETH_ALEN];
+
+ /* IEEE 802.11u - Roaming Consortium list */
+ unsigned int roaming_consortium_count;
+ struct hostapd_roaming_consortium *roaming_consortium;
+
+ /* IEEE 802.11u - Venue Name duples */
+ unsigned int venue_name_count;
+ struct hostapd_lang_string *venue_name;
+
+ /* Venue URL duples */
+ unsigned int venue_url_count;
+ struct hostapd_venue_url *venue_url;
+
+ /* IEEE 802.11u - Network Authentication Type */
+ u8 *network_auth_type;
+ size_t network_auth_type_len;
+
+ /* IEEE 802.11u - IP Address Type Availability */
+ u8 ipaddr_type_availability;
+ u8 ipaddr_type_configured;
+
+ /* IEEE 802.11u - 3GPP Cellular Network */
+ u8 *anqp_3gpp_cell_net;
+ size_t anqp_3gpp_cell_net_len;
+
+ /* IEEE 802.11u - Domain Name */
+ u8 *domain_name;
+ size_t domain_name_len;
+
+ unsigned int nai_realm_count;
+ struct hostapd_nai_realm_data *nai_realm_data;
+
+ struct dl_list anqp_elem; /* list of struct anqp_element */
+
+ u16 gas_comeback_delay;
+ size_t gas_frag_limit;
+ int gas_address3;
+
+ u8 qos_map_set[16 + 2 * 21];
+ unsigned int qos_map_set_len;
+
+ int osen;
+ int proxy_arp;
+ int na_mcast_to_ucast;
+
+#ifdef CONFIG_HS20
+ int hs20;
+ int hs20_release;
+ int disable_dgaf;
+ u16 anqp_domain_id;
+ unsigned int hs20_oper_friendly_name_count;
+ struct hostapd_lang_string *hs20_oper_friendly_name;
+ u8 *hs20_wan_metrics;
+ u8 *hs20_connection_capability;
+ size_t hs20_connection_capability_len;
+ u8 *hs20_operating_class;
+ u8 hs20_operating_class_len;
+ struct hs20_icon {
+ u16 width;
+ u16 height;
+ char language[3];
+ char type[256];
+ char name[256];
+ char file[256];
+ } *hs20_icons;
+ size_t hs20_icons_count;
+ u8 osu_ssid[SSID_MAX_LEN];
+ size_t osu_ssid_len;
+ struct hs20_osu_provider {
+ unsigned int friendly_name_count;
+ struct hostapd_lang_string *friendly_name;
+ char *server_uri;
+ int *method_list;
+ char **icons;
+ size_t icons_count;
+ char *osu_nai;
+ char *osu_nai2;
+ unsigned int service_desc_count;
+ struct hostapd_lang_string *service_desc;
+ } *hs20_osu_providers, *last_osu;
+ size_t hs20_osu_providers_count;
+ size_t hs20_osu_providers_nai_count;
+ char **hs20_operator_icon;
+ size_t hs20_operator_icon_count;
+ unsigned int hs20_deauth_req_timeout;
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
+ char *hs20_sim_provisioning_url;
+ char *t_c_filename;
+ u32 t_c_timestamp;
+ char *t_c_server_url;
+#endif /* CONFIG_HS20 */
+
+ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
+
+#ifdef CONFIG_RADIUS_TEST
+ char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+ struct wpabuf *vendor_elements;
+ struct wpabuf *assocresp_elements;
+
+ unsigned int anti_clogging_threshold;
+ unsigned int sae_sync;
+ int sae_require_mfp;
+ int sae_confirm_immediate;
+ enum sae_pwe sae_pwe;
+ int *sae_groups;
+ struct sae_password_entry *sae_passwords;
+
+ char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 bss_load_test[5];
+ u8 bss_load_test_set;
+ struct wpabuf *own_ie_override;
+ int sae_reflection_attack;
+ int sae_commit_status;
+ int sae_pk_omit;
+ int sae_pk_password_check_skip;
+ struct wpabuf *sae_commit_override;
+ struct wpabuf *rsne_override_eapol;
+ struct wpabuf *rsnxe_override_eapol;
+ struct wpabuf *rsne_override_ft;
+ struct wpabuf *rsnxe_override_ft;
+ struct wpabuf *gtk_rsc_override;
+ struct wpabuf *igtk_rsc_override;
+ int no_beacon_rsnxe;
+ int skip_prune_assoc;
+ int ft_rsnxe_used;
+ unsigned int oci_freq_override_eapol_m3;
+ unsigned int oci_freq_override_eapol_g1;
+ unsigned int oci_freq_override_saquery_req;
+ unsigned int oci_freq_override_saquery_resp;
+ unsigned int oci_freq_override_ft_assoc;
+ unsigned int oci_freq_override_fils_assoc;
+ unsigned int oci_freq_override_wnm_sleep;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+ int mesh;
+ int mesh_fwding;
+
+ u8 radio_measurements[RRM_CAPABILITIES_IE_LEN];
+
+ int vendor_vht;
+ int use_sta_nsts;
+
+ char *no_probe_resp_if_seen_on;
+ char *no_auth_if_seen_on;
+
+ int pbss;
+
+#ifdef CONFIG_MBO
+ int mbo_enabled;
+ /**
+ * oce - Enable OCE in AP and/or STA-CFON mode
+ * - BIT(0) is Reserved
+ * - Set BIT(1) to enable OCE in STA-CFON mode
+ * - Set BIT(2) to enable OCE in AP mode
+ */
+ unsigned int oce;
+ int mbo_cell_data_conn_pref;
+#endif /* CONFIG_MBO */
+
+ int ftm_responder;
+ int ftm_initiator;
+
+#ifdef CONFIG_FILS
+ u8 fils_cache_id[FILS_CACHE_ID_LEN];
+ int fils_cache_id_set;
+ struct dl_list fils_realms; /* list of struct fils_realm */
+ int fils_dh_group;
+ struct hostapd_ip_addr dhcp_server;
+ int dhcp_rapid_commit_proxy;
+ unsigned int fils_hlp_wait_time;
+ u16 dhcp_server_port;
+ u16 dhcp_relay_port;
+ u32 fils_discovery_min_int;
+ u32 fils_discovery_max_int;
+#endif /* CONFIG_FILS */
+
+ int multicast_to_unicast;
+ int bridge_multicast_to_unicast;
+
+ int broadcast_deauth;
+
+ int notify_mgmt_frames;
+
+#ifdef CONFIG_DPP
+ char *dpp_name;
+ char *dpp_mud_url;
+ char *dpp_extra_conf_req_name;
+ char *dpp_extra_conf_req_value;
+ char *dpp_connector;
+ struct wpabuf *dpp_netaccesskey;
+ unsigned int dpp_netaccesskey_expiry;
+ struct wpabuf *dpp_csign;
+#ifdef CONFIG_DPP2
+ struct dpp_controller_conf *dpp_controller;
+ int dpp_relay_port;
+ int dpp_configurator_connectivity;
+ int dpp_pfs;
+#endif /* CONFIG_DPP2 */
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_OWE
+ macaddr owe_transition_bssid;
+ u8 owe_transition_ssid[SSID_MAX_LEN];
+ size_t owe_transition_ssid_len;
+ char owe_transition_ifname[IFNAMSIZ + 1];
+ int *owe_groups;
+ int owe_ptk_workaround;
+#endif /* CONFIG_OWE */
+
+ int coloc_intf_reporting;
+
+ u8 send_probe_response;
+
+ u8 transition_disable;
+
+#define BACKHAUL_BSS 1
+#define FRONTHAUL_BSS 2
+ int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+
+#ifdef CONFIG_AIRTIME_POLICY
+ unsigned int airtime_weight;
+ int airtime_limit;
+ struct airtime_sta_weight *airtime_weight_list;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_MACSEC
+ /**
+ * macsec_policy - Determines the policy for MACsec secure session
+ *
+ * 0: MACsec not in use (default)
+ * 1: MACsec enabled - Should secure, accept key server's advice to
+ * determine whether to use a secure session or not.
+ */
+ int macsec_policy;
+
+ /**
+ * macsec_integ_only - Determines how MACsec are transmitted
+ *
+ * This setting applies only when MACsec is in use, i.e.,
+ * - macsec_policy is enabled
+ * - the key server has decided to enable MACsec
+ *
+ * 0: Encrypt traffic (default)
+ * 1: Integrity only
+ */
+ int macsec_integ_only;
+
+ /**
+ * macsec_replay_protect - Enable MACsec replay protection
+ *
+ * This setting applies only when MACsec is in use, i.e.,
+ * - macsec_policy is enabled
+ * - the key server has decided to enable MACsec
+ *
+ * 0: Replay protection disabled (default)
+ * 1: Replay protection enabled
+ */
+ int macsec_replay_protect;
+
+ /**
+ * macsec_replay_window - MACsec replay protection window
+ *
+ * A window in which replay is tolerated, to allow receipt of frames
+ * that have been misordered by the network.
+ *
+ * This setting applies only when MACsec replay protection active, i.e.,
+ * - macsec_replay_protect is enabled
+ * - the key server has decided to enable MACsec
+ *
+ * 0: No replay window, strict check (default)
+ * 1..2^32-1: number of packets that could be misordered
+ */
+ u32 macsec_replay_window;
+
+ /**
+ * macsec_offload - Enable MACsec offload
+ *
+ * This setting applies only when MACsec is in use, i.e.,
+ * - macsec_policy is enabled
+ * - the key server has decided to enable MACsec
+ *
+ * 0 = MACSEC_OFFLOAD_OFF (default)
+ * 1 = MACSEC_OFFLOAD_PHY
+ * 2 = MACSEC_OFFLOAD_MAC
+ */
+ int macsec_offload;
+
+ /**
+ * macsec_port - MACsec port (in SCI)
+ *
+ * Port component of the SCI.
+ *
+ * Range: 1-65534 (default: 1)
+ */
+ int macsec_port;
+
+ /**
+ * mka_priority - Priority of MKA Actor
+ *
+ * Range: 0-255 (default: 255)
+ */
+ int mka_priority;
+
+ /**
+ * macsec_csindex - Cipher suite index for MACsec
+ *
+ * Range: 0-1 (default: 0)
+ */
+ int macsec_csindex;
+
+ /**
+ * mka_ckn - MKA pre-shared CKN
+ */
+#define MACSEC_CKN_MAX_LEN 32
+ size_t mka_ckn_len;
+ u8 mka_ckn[MACSEC_CKN_MAX_LEN];
+
+ /**
+ * mka_cak - MKA pre-shared CAK
+ */
+#define MACSEC_CAK_MAX_LEN 32
+ size_t mka_cak_len;
+ u8 mka_cak[MACSEC_CAK_MAX_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+ /**
+ * mka_psk_set - Whether mka_ckn and mka_cak are set
+ */
+ u8 mka_psk_set;
+#endif /* CONFIG_MACSEC */
+
+#ifdef CONFIG_PASN
+ /* Whether to allow PASN-UNAUTH */
+ int pasn_noauth;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /*
+ * Normally, KDK should be derived if and only if both sides support
+ * secure LTF. Allow forcing KDK derivation for testing purposes.
+ */
+ int force_kdk_derivation;
+
+ /* If set, corrupt the MIC in the 2nd Authentication frame of PASN */
+ int pasn_corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ int *pasn_groups;
+
+ /*
+ * The time in TUs after which the non-AP STA is requested to retry the
+ * PASN authentication in case there are too many parallel operations.
+ */
+ u16 pasn_comeback_after;
+#endif /* CONFIG_PASN */
+
+ unsigned int unsol_bcast_probe_resp_interval;
+
+ u8 ext_capa_mask[EXT_CAPA_MAX_LEN];
+ u8 ext_capa[EXT_CAPA_MAX_LEN];
+
+ u8 rnr;
+ char *config_id;
+ bool xrates_supported;
+
+#ifdef CONFIG_IEEE80211BE
+ /* The AP is part of an AP MLD */
+ u8 mld_ap;
+
+ /* The MLD ID to which the AP MLD is affiliated with */
+ u8 mld_id;
+#endif /* CONFIG_IEEE80211BE */
+};
+
+/**
+ * struct he_phy_capabilities_info - HE PHY capabilities
+ */
+struct he_phy_capabilities_info {
+ bool he_su_beamformer;
+ bool he_su_beamformee;
+ bool he_mu_beamformer;
+};
+
+/**
+ * struct he_operation - HE operation
+ */
+struct he_operation {
+ u8 he_bss_color;
+ u8 he_bss_color_disabled;
+ u8 he_bss_color_partial;
+ u8 he_default_pe_duration;
+ u8 he_twt_required;
+ u8 he_twt_responder;
+ u16 he_rts_threshold;
+ u8 he_er_su_disable;
+ u16 he_basic_mcs_nss_set;
+};
+
+/**
+ * struct spatial_reuse - Spatial reuse
+ */
+struct spatial_reuse {
+ u8 sr_control;
+ u8 non_srg_obss_pd_max_offset;
+ u8 srg_obss_pd_min_offset;
+ u8 srg_obss_pd_max_offset;
+ u8 srg_bss_color_bitmap[8];
+ u8 srg_partial_bssid_bitmap[8];
+};
+
+/**
+ * struct eht_phy_capabilities_info - EHT PHY capabilities
+ */
+struct eht_phy_capabilities_info {
+ bool su_beamformer;
+ bool su_beamformee;
+ bool mu_beamformer;
+};
+
+/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+struct hostapd_config {
+ struct hostapd_bss_config **bss, *last_bss;
+ size_t num_bss;
+ char *config_id;
+
+ u16 beacon_int;
+ int rts_threshold;
+ int fragm_threshold;
+ u8 op_class;
+ u8 channel;
+ int enable_edmg;
+ u8 edmg_channel;
+ u8 acs;
+ struct wpa_freq_range_list acs_ch_list;
+ struct wpa_freq_range_list acs_freq_list;
+ u8 acs_freq_list_present;
+ int acs_exclude_dfs;
+ u8 min_tx_power;
+ enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+ bool hw_mode_set;
+ int acs_exclude_6ghz_non_psc;
+ int enable_background_radar;
+ enum {
+ LONG_PREAMBLE = 0,
+ SHORT_PREAMBLE = 1
+ } preamble;
+
+ int *supported_rates;
+ int *basic_rates;
+ unsigned int beacon_rate;
+ enum beacon_rate_type rate_type;
+
+ const struct wpa_driver_ops *driver;
+ char *driver_params;
+
+ int ap_table_max_size;
+ int ap_table_expiration_time;
+
+ unsigned int track_sta_max_num;
+ unsigned int track_sta_max_age;
+
+ int max_num_sta;
+
+ char country[3]; /* first two octets: country code as described in
+ * ISO/IEC 3166-1. Third octet:
+ * ' ' (ascii 32): all environments
+ * 'O': Outdoor environemnt only
+ * 'I': Indoor environment only
+ * 'X': Used with noncountry entity ("XXX")
+ * 0x00..0x31: identifying IEEE 802.11 standard
+ * Annex E table (0x04 = global table)
+ */
+
+ int ieee80211d;
+
+ int ieee80211h; /* DFS */
+
+ /*
+ * Local power constraint is an octet encoded as an unsigned integer in
+ * units of decibels. Invalid value -1 indicates that Power Constraint
+ * element will not be added.
+ */
+ int local_pwr_constraint;
+
+ /* Control Spectrum Management bit */
+ int spectrum_mgmt_required;
+
+ struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
+
+ /*
+ * WMM AC parameters, in same order as 802.1D, i.e.
+ * 0 = BE (best effort)
+ * 1 = BK (background)
+ * 2 = VI (video)
+ * 3 = VO (voice)
+ */
+ struct hostapd_wmm_ac_params wmm_ac_params[4];
+#ifdef CONFIG_DFS_CAC_TIMER
+ /* Yewei, add dfs cac timer in seconds, 20240118 */
+ int dfs_cac_s;
+#endif /* CONFIG_DFS_CAC_TIMER */
+ int ht_op_mode_fixed;
+ u16 ht_capab;
+ int noscan;
+ int no_ht_coex;
+ int ieee80211n;
+ int secondary_channel;
+ int no_pri_sec_switch;
+ int require_ht;
+ int obss_interval;
+ u32 vht_capab;
+ int ieee80211ac;
+ int require_vht;
+ enum oper_chan_width vht_oper_chwidth;
+ u8 vht_oper_centr_freq_seg0_idx;
+ u8 vht_oper_centr_freq_seg1_idx;
+ u8 ht40_plus_minus_allowed;
+
+#ifdef CONFIG_24G_BW_SWITCH
+ /* yewei, add for enable 20M/40M switch, 20240118 */
+ int auto_20m_40m;
+#endif /* CONFIG_24G_BW_SWITCH */
+#ifdef CONFIG_5G_BW_SWITCH
+ // Add for auto 20M/40M/80M bandwidth switch. Added by Liangyu Chu
+ int auto_80m;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+ /* Use driver-generated interface addresses when adding multiple BSSs */
+ u8 use_driver_iface_addr;
+
+#ifdef CONFIG_FST
+ struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_P2P
+ u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ double ignore_probe_probability;
+ double ignore_auth_probability;
+ double ignore_assoc_probability;
+ double ignore_reassoc_probability;
+ double corrupt_gtk_rekey_mic_probability;
+ int ecsa_ie_only;
+ bool delay_eapol_tx;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_scans;
+ struct acs_bias {
+ int channel;
+ double bias;
+ } *acs_chan_bias;
+ unsigned int num_acs_chan_bias;
+#endif /* CONFIG_ACS */
+
+ struct wpabuf *lci;
+ struct wpabuf *civic;
+ int stationary_ap;
+
+ int ieee80211ax;
+#ifdef CONFIG_IEEE80211AX
+ struct he_phy_capabilities_info he_phy_capab;
+ struct he_operation he_op;
+ struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
+ struct spatial_reuse spr;
+ enum oper_chan_width he_oper_chwidth;
+ u8 he_oper_centr_freq_seg0_idx;
+ u8 he_oper_centr_freq_seg1_idx;
+ u8 he_6ghz_max_mpdu;
+ u8 he_6ghz_max_ampdu_len_exp;
+ u8 he_6ghz_rx_ant_pat;
+ u8 he_6ghz_tx_ant_pat;
+ u8 he_6ghz_reg_pwr_type;
+ bool require_he;
+#endif /* CONFIG_IEEE80211AX */
+
+ /* VHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_VHT_ENABLED BIT(0)
+#define CH_SWITCH_VHT_DISABLED BIT(1)
+ unsigned int ch_switch_vht_config;
+
+ /* HE enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_HE_ENABLED BIT(0)
+#define CH_SWITCH_HE_DISABLED BIT(1)
+ unsigned int ch_switch_he_config;
+
+ int rssi_reject_assoc_rssi;
+ int rssi_reject_assoc_timeout;
+ int rssi_ignore_probe_request;
+
+#ifdef CONFIG_AIRTIME_POLICY
+ enum {
+ AIRTIME_MODE_OFF = 0,
+ AIRTIME_MODE_STATIC = 1,
+ AIRTIME_MODE_DYNAMIC = 2,
+ AIRTIME_MODE_LIMIT = 3,
+ __AIRTIME_MODE_MAX,
+ } airtime_mode;
+ unsigned int airtime_update_interval;
+#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
+#endif /* CONFIG_AIRTIME_POLICY */
+
+ int ieee80211be;
+#ifdef CONFIG_IEEE80211BE
+ enum oper_chan_width eht_oper_chwidth;
+ u8 eht_oper_centr_freq_seg0_idx;
+ struct eht_phy_capabilities_info eht_phy_capab;
+ u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
+ u8 punct_acs_threshold;
+#endif /* CONFIG_IEEE80211BE */
+
+ /* EHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_EHT_ENABLED BIT(0)
+#define CH_SWITCH_EHT_DISABLED BIT(1)
+ unsigned int ch_switch_eht_config;
+
+ enum mbssid {
+ MBSSID_DISABLED = 0,
+ MBSSID_ENABLED = 1,
+ ENHANCED_MBSSID_ENABLED = 2,
+ } mbssid;
+};
+
+
+static inline enum oper_chan_width
+hostapd_get_oper_chwidth(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ return conf->eht_oper_chwidth;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax)
+ return conf->he_oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+ return conf->vht_oper_chwidth;
+}
+
+static inline void
+hostapd_set_oper_chwidth(struct hostapd_config *conf,
+ enum oper_chan_width oper_chwidth)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ conf->eht_oper_chwidth = oper_chwidth;
+ if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+ oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax)
+ conf->he_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+ conf->vht_oper_chwidth = oper_chwidth;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ return conf->eht_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax)
+ return conf->he_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+ return conf->vht_oper_centr_freq_seg0_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
+ u8 oper_centr_freq_seg0_idx)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
+ oper_centr_freq_seg0_idx +=
+ conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax)
+ conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+ conf->vht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg1_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax)
+ return conf->he_oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+ return conf->vht_oper_centr_freq_seg1_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
+ u8 oper_centr_freq_seg1_idx)
+{
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax)
+ conf->he_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+ conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b);
+struct hostapd_config * hostapd_config_defaults(void);
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr);
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_free_bss(struct hostapd_bss_config *conf);
+void hostapd_config_free(struct hostapd_config *conf);
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+ const u8 *addr, struct vlan_description *vlan_id);
+int hostapd_rate_found(int *list, int rate);
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk, int *vlan_id);
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+ struct vlan_description *vlan_desc);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+ int vlan_id);
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value);
+int hostapd_config_check(struct hostapd_config *conf, int full_config);
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+ int full_config);
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
+int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
+int hostapd_acl_comp(const void *a, const void *b);
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+ int vlan_id, const u8 *addr);
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+ const u8 *addr);
+
+#endif /* HOSTAPD_CONFIG_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.c
new file mode 100644
index 0000000..75ddfa1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.c
@@ -0,0 +1,1139 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
+#include "wps/wps.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "hs20.h"
+#include "wpa_auth.h"
+#include "ap_drv_ops.h"
+
+
+u32 hostapd_sta_flags_to_drv(u32 flags)
+{
+ int res = 0;
+ if (flags & WLAN_STA_AUTHORIZED)
+ res |= WPA_STA_AUTHORIZED;
+ if (flags & WLAN_STA_WMM)
+ res |= WPA_STA_WMM;
+ if (flags & WLAN_STA_SHORT_PREAMBLE)
+ res |= WPA_STA_SHORT_PREAMBLE;
+ if (flags & WLAN_STA_MFP)
+ res |= WPA_STA_MFP;
+ if (flags & WLAN_STA_AUTH)
+ res |= WPA_STA_AUTHENTICATED;
+ if (flags & WLAN_STA_ASSOC)
+ res |= WPA_STA_ASSOCIATED;
+ return res;
+}
+
+
+static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
+{
+ if (!src)
+ return 0;
+ if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
+ return -1;
+ wpabuf_put_buf(*dst, src);
+ return 0;
+}
+
+
+static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
+{
+ if (!data || !len)
+ return 0;
+ if (wpabuf_resize(dst, len) != 0)
+ return -1;
+ wpabuf_put_data(*dst, data, len);
+ return 0;
+}
+
+
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon_ret,
+ struct wpabuf **proberesp_ret,
+ struct wpabuf **assocresp_ret)
+{
+ struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+ u8 buf[200], *pos;
+
+ *beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
+
+#ifdef NEED_AP_MLME
+ pos = buf;
+ pos = hostapd_eid_rm_enabled_capab(hapd, pos, sizeof(buf));
+ if (add_buf_data(&assocresp, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* NEED_AP_MLME */
+
+ pos = buf;
+ pos = hostapd_eid_time_adv(hapd, pos);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0)
+ goto fail;
+ pos = hostapd_eid_time_zone(hapd, pos);
+ if (add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+
+ pos = buf;
+ pos = hostapd_eid_ext_capab(hapd, pos, false);
+ if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+ goto fail;
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+
+#ifdef CONFIG_FST
+ if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
+ add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
+ add_buf(&assocresp, hapd->iface->fst_ies) < 0)
+ goto fail;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_FILS
+ pos = hostapd_eid_fils_indic(hapd, buf, 0);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* CONFIG_FILS */
+
+ pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
+ if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+ goto fail;
+
+ if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
+ add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
+ goto fail;
+
+#ifdef CONFIG_P2P
+ if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
+ add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
+ goto fail;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&beacon, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(beacon, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(beacon, p - start);
+ }
+
+ if (wpabuf_resize(&proberesp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(proberesp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(proberesp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state) {
+ struct wpabuf *a = wps_build_assoc_resp_ie();
+ add_buf(&assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&assocresp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(assocresp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(assocresp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (hapd->p2p_group) {
+ struct wpabuf *a;
+ a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+ add_buf(&assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_HS20
+ pos = hostapd_eid_hs20_indication(hapd, buf);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+
+ pos = hostapd_eid_osen(hapd, buf);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled ||
+ OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
+ pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0 ||
+ add_buf_data(&assocresp, buf, pos - buf) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_OWE
+ pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* CONFIG_OWE */
+
+ add_buf(&beacon, hapd->conf->vendor_elements);
+ add_buf(&proberesp, hapd->conf->vendor_elements);
+ add_buf(&assocresp, hapd->conf->assocresp_elements);
+
+ *beacon_ret = beacon;
+ *proberesp_ret = proberesp;
+ *assocresp_ret = assocresp;
+
+ return 0;
+
+fail:
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
+ return -1;
+}
+
+
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp)
+{
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
+}
+
+
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+ return 0;
+
+ return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
+}
+
+
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int ret;
+
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+ return 0;
+
+ if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+ 0)
+ return -1;
+
+ ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+ assocresp);
+
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+
+ return ret;
+}
+
+
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ if (authorized) {
+ return hostapd_sta_set_flags(hapd, sta->addr,
+ hostapd_sta_flags_to_drv(
+ sta->flags),
+ WPA_STA_AUTHORIZED, ~0);
+ }
+
+ return hostapd_sta_set_flags(hapd, sta->addr,
+ hostapd_sta_flags_to_drv(sta->flags),
+ 0, ~WPA_STA_AUTHORIZED);
+}
+
+
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int set_flags, total_flags, flags_and, flags_or;
+ total_flags = hostapd_sta_flags_to_drv(sta->flags);
+ set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
+ if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+ sta->auth_alg == WLAN_AUTH_FT) &&
+ sta->flags & WLAN_STA_AUTHORIZED)
+ set_flags |= WPA_STA_AUTHORIZED;
+ flags_or = total_flags & set_flags;
+ flags_and = total_flags | ~set_flags;
+ return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+ flags_or, flags_and);
+}
+
+
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled)
+{
+ struct wpa_bss_params params;
+ os_memset(¶ms, 0, sizeof(params));
+ params.ifname = ifname;
+ params.enabled = enabled;
+ if (enabled) {
+ params.wpa = hapd->conf->wpa;
+ params.ieee802_1x = hapd->conf->ieee802_1x;
+ params.wpa_group = hapd->conf->wpa_group;
+ if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+ (WPA_PROTO_WPA | WPA_PROTO_RSN))
+ params.wpa_pairwise = hapd->conf->wpa_pairwise |
+ hapd->conf->rsn_pairwise;
+ else if (hapd->conf->wpa & WPA_PROTO_RSN)
+ params.wpa_pairwise = hapd->conf->rsn_pairwise;
+ else if (hapd->conf->wpa & WPA_PROTO_WPA)
+ params.wpa_pairwise = hapd->conf->wpa_pairwise;
+ params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+ params.rsn_preauth = hapd->conf->rsn_preauth;
+ params.ieee80211w = hapd->conf->ieee80211w;
+ }
+ return hostapd_set_ieee8021x(hapd, ¶ms);
+}
+
+
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+{
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+ NULL, NULL, force_ifname, if_addr, NULL, 0);
+}
+
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
+{
+ return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
+}
+
+
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ const u8 *addr, int aid, int val)
+{
+ const char *bridge = NULL;
+
+ if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+ return -1;
+ if (hapd->conf->wds_bridge[0])
+ bridge = hapd->conf->wds_bridge;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ bridge, ifname_wds);
+}
+
+
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg)
+{
+ if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
+ return -EOPNOTSUPP;
+ return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
+}
+
+
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len)
+{
+ struct wpa_driver_sta_auth_params params;
+#ifdef CONFIG_FILS
+ struct sta_info *sta;
+#endif /* CONFIG_FILS */
+
+ if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for sta_auth processing",
+ MAC2STR(addr));
+ return 0;
+ }
+
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ params.fils_auth = 1;
+ wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
+ params.fils_snonce,
+ params.fils_kek,
+ ¶ms.fils_kek_len);
+ }
+#endif /* CONFIG_FILS */
+
+ params.own_addr = hapd->own_addr;
+ params.addr = addr;
+ params.seq = seq;
+ params.status = status;
+ params.ie = ie;
+ params.len = len;
+
+ return hapd->driver->sta_auth(hapd->drv_priv, ¶ms);
+}
+
+
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
+ return 0;
+ return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+ reassoc, status, ie, len);
+}
+
+
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ const struct ieee80211_he_capabilities *he_capab,
+ size_t he_capab_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+ int set, const u8 *link_addr, bool mld_link_sta)
+{
+ struct hostapd_sta_add_params params;
+
+ if (hapd->driver == NULL)
+ return 0;
+ if (hapd->driver->sta_add == NULL)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.addr = addr;
+ params.aid = aid;
+ params.capability = capability;
+ params.supp_rates = supp_rates;
+ params.supp_rates_len = supp_rates_len;
+ params.listen_interval = listen_interval;
+ params.ht_capabilities = ht_capab;
+ params.vht_capabilities = vht_capab;
+ params.he_capab = he_capab;
+ params.he_capab_len = he_capab_len;
+ params.eht_capab = eht_capab;
+ params.eht_capab_len = eht_capab_len;
+ params.he_6ghz_capab = he_6ghz_capab;
+ params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+ params.vht_opmode = vht_opmode;
+ params.flags = hostapd_sta_flags_to_drv(flags);
+ params.qosinfo = qosinfo;
+ params.support_p2p_ps = supp_p2p_ps;
+ params.set = set;
+ params.mld_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * An AP MLD needs to always specify to what link the station needs
+ * to be added.
+ */
+ if (hapd->conf->mld_ap) {
+ params.mld_link_id = hapd->mld_link_id;
+ params.mld_link_addr = link_addr;
+ params.mld_link_sta = mld_link_sta;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
+}
+
+
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+{
+ if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
+ return 0;
+ return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+ tspec_ielen);
+}
+
+
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
+{
+ if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
+ return 0;
+ return hapd->driver->set_privacy(hapd->drv_priv, enabled);
+}
+
+
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ size_t elem_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
+ return 0;
+ return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
+}
+
+
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
+ return 0;
+ return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
+ return 0;
+ return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing)
+{
+ if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ return -1;
+ return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+ bss_ctx, drv_priv, force_ifname, if_addr,
+ bridge, use_existing, 1);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->if_remove == NULL)
+ return -1;
+ return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ struct wpa_bss_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+ return 0;
+ return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+ const u8 *addr, int idx, int link_id, u8 *seq)
+{
+ if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+ return 0;
+ return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
+ link_id, seq);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->flush == NULL)
+ return 0;
+ return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+ int freq, int channel, int edmg, u8 edmg_channel,
+ int ht_enabled, int vht_enabled,
+ int he_enabled, bool eht_enabled,
+ int sec_channel_offset, int oper_chwidth,
+ int center_segment0, int center_segment1)
+{
+ struct hostapd_freq_params data;
+ struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, edmg,
+ edmg_channel, ht_enabled,
+ vht_enabled, he_enabled, eht_enabled,
+ sec_channel_offset, oper_chwidth,
+ center_segment0, center_segment1,
+ cmode ? cmode->vht_capab : 0,
+ cmode ?
+ &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
+ cmode ?
+ &cmode->eht_capab[IEEE80211_MODE_AP] :
+ NULL))
+ return -1;
+
+ if (hapd->driver == NULL)
+ return 0;
+ if (hapd->driver->set_freq == NULL)
+ return 0;
+
+ data.link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ data.link_id = hapd->mld_link_id;
+ wpa_printf(MSG_DEBUG,
+ "hostapd_set_freq: link_id=%d", data.link_id);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+int hostapd_set_rts(struct hostapd_data *hapd, int rts)
+{
+ if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
+ return 0;
+ return hapd->driver->set_rts(hapd->drv_priv, rts);
+}
+
+
+int hostapd_set_frag(struct hostapd_data *hapd, int frag)
+{
+ if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
+ return 0;
+ return hapd->driver->set_frag(hapd->drv_priv, frag);
+}
+
+
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->sta_set_flags)
+ return 0;
+ return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
+ flags_or, flags_and);
+}
+
+
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+ unsigned int weight)
+{
+ if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
+ return 0;
+ return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
+ weight);
+}
+
+
+int hostapd_set_country(struct hostapd_data *hapd, const char *country)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_country == NULL)
+ return 0;
+ return hapd->driver->set_country(hapd->drv_priv, country);
+}
+
+
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time)
+{
+ int link_id = -1;
+
+ if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
+ return 0;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
+ cw_min, cw_max, burst_time,
+ link_id);
+}
+
+
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+ u16 *flags, u8 *dfs_domain)
+{
+ if (!hapd->driver || !hapd->driver->get_hw_feature_data ||
+ !hapd->drv_priv)
+ return NULL;
+ return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+ flags, dfs_domain);
+}
+
+
+int hostapd_driver_commit(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->commit == NULL)
+ return 0;
+ return hapd->driver->commit(hapd->drv_priv);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+ return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+bool hostapd_drv_nl80211(struct hostapd_data *hapd)
+{
+ return hapd->driver && os_strcmp(hapd->driver->name, "nl80211") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+ struct wpa_driver_scan_params *params)
+{
+ if (hapd->driver && hapd->driver->scan2)
+ return hapd->driver->scan2(hapd->drv_priv, params);
+ return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+ struct hostapd_data *hapd)
+{
+ if (hapd->driver && hapd->driver->get_scan_results2)
+ return hapd->driver->get_scan_results2(hapd->drv_priv);
+ return NULL;
+}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ if (hapd->driver && hapd->driver->set_noa)
+ return hapd->driver->set_noa(hapd->drv_priv, count, start,
+ duration);
+ return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int vlan_id, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len, enum key_flag key_flag)
+{
+ struct wpa_driver_set_key_params params;
+
+ if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.ifname = ifname;
+ params.alg = alg;
+ params.addr = addr;
+ params.key_idx = key_idx;
+ params.set_tx = set_tx;
+ params.seq = seq;
+ params.seq_len = seq_len;
+ params.key = key;
+ params.key_len = key_len;
+ params.vlan_id = vlan_id;
+ params.key_flag = key_flag;
+ params.link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && !(key_flag & KEY_FLAG_PAIRWISE))
+ params.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->driver->set_key(hapd->drv_priv, ¶ms);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack,
+ const u16 *csa_offs, size_t csa_offs_len,
+ int no_encrypt)
+{
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+ csa_offs, csa_offs_len, no_encrypt, 0,
+ link_id);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ reason, link_id);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
+
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
+ const u8 *peer, u8 *buf, u16 *buf_len)
+{
+ if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
+ return -1;
+ return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
+ buf_len);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len)
+{
+ const u8 *bssid;
+ const u8 wildcard_bssid[ETH_ALEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
+ return 0;
+ bssid = hapd->own_addr;
+ if (!is_multicast_ether_addr(dst) &&
+ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+ struct sta_info *sta;
+
+ /*
+ * Public Action frames to a STA that is not a member of the BSS
+ * shall use wildcard BSSID value.
+ */
+ sta = ap_get_sta(hapd, dst);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC))
+ bssid = wildcard_bssid;
+ } else if (is_broadcast_ether_addr(dst) &&
+ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+ /*
+ * The only current use case of Public Action frames with
+ * broadcast destination address is DPP PKEX. That case is
+ * directing all devices and not just the STAs within the BSS,
+ * so have to use the wildcard BSSID value.
+ */
+ bssid = wildcard_bssid;
+ }
+ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+ hapd->own_addr, bssid, data, len, 0);
+}
+
+
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int wait, const u8 *dst,
+ const u8 *data, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+ return 0;
+ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+ hapd->own_addr, hapd->own_addr, data,
+ len, 0);
+}
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ enum hostapd_hw_mode mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int he_enabled, bool eht_enabled,
+ int sec_channel_offset, int oper_chwidth,
+ int center_segment0, int center_segment1,
+ bool radar_background)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_freq_params data;
+ int res;
+ struct hostapd_hw_modes *cmode = iface->current_mode;
+
+ if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
+ return 0;
+
+ if (!iface->conf->ieee80211h) {
+ wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+ "is not enabled");
+ return -1;
+ }
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, 0, 0,
+ ht_enabled,
+ vht_enabled, he_enabled, eht_enabled,
+ sec_channel_offset,
+ oper_chwidth, center_segment0,
+ center_segment1,
+ cmode->vht_capab,
+ &cmode->he_capab[IEEE80211_MODE_AP],
+ &cmode->eht_capab[IEEE80211_MODE_AP])) {
+ wpa_printf(MSG_ERROR, "Can't set freq params");
+ return -1;
+ }
+ data.radar_background = radar_background;
+
+ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ if (!res) {
+ if (radar_background)
+ iface->radar_background.cac_started = 1;
+ else
+ iface->cac_started = 1;
+ os_get_reltime(&iface->dfs_cac_start);
+ }
+
+ return res;
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+{
+ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+}
+
+
+void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode,
+ int acs_ch_list_all, bool allow_disabled,
+ int **freq_list)
+{
+ int i;
+ bool is_no_ir = false;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if (!acs_ch_list_all &&
+ (hapd->iface->conf->acs_freq_list.num &&
+ !freq_range_list_includes(
+ &hapd->iface->conf->acs_freq_list,
+ chan->freq)))
+ continue;
+ if (!acs_ch_list_all &&
+ (!hapd->iface->conf->acs_freq_list_present &&
+ hapd->iface->conf->acs_ch_list.num &&
+ !freq_range_list_includes(
+ &hapd->iface->conf->acs_ch_list,
+ chan->chan)))
+ continue;
+ if (is_6ghz_freq(chan->freq) &&
+ ((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+ !is_6ghz_psc_frequency(chan->freq)) ||
+ (!hapd->iface->conf->ieee80211ax &&
+ !hapd->iface->conf->ieee80211be)))
+ continue;
+ if ((!(chan->flag & HOSTAPD_CHAN_DISABLED) || allow_disabled) &&
+ !(hapd->iface->conf->acs_exclude_dfs &&
+ (chan->flag & HOSTAPD_CHAN_RADAR)) &&
+ !(chan->max_tx_power < hapd->iface->conf->min_tx_power))
+ int_array_add_unique(freq_list, chan->freq);
+ else if ((chan->flag & HOSTAPD_CHAN_NO_IR) &&
+ is_6ghz_freq(chan->freq))
+ is_no_ir = true;
+ }
+
+ hapd->iface->is_no_ir = is_no_ir;
+}
+
+
+void hostapd_get_ext_capa(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+
+ if (!hapd->driver || !hapd->driver->get_ext_capab)
+ return;
+
+ hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
+ &iface->extended_capa,
+ &iface->extended_capa_mask,
+ &iface->extended_capa_len);
+}
+
+
+void hostapd_get_mld_capa(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+
+ if (!hapd->driver || !hapd->driver->get_mld_capab)
+ return;
+
+ hapd->driver->get_mld_capab(hapd->drv_priv, WPA_IF_AP_BSS,
+ &iface->mld_eml_capa,
+ &iface->mld_mld_capa);
+}
+
+
+/**
+ * hostapd_drv_do_acs - Start automatic channel selection
+ * @hapd: BSS data for the device initiating ACS
+ * Returns: 0 on success, -1 on failure, 1 on failure due to NO_IR (AFC)
+ */
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+ struct drv_acs_params params;
+ int ret, i, acs_ch_list_all = 0;
+ struct hostapd_hw_modes *mode;
+ int *freq_list = NULL;
+ enum hostapd_hw_mode selected_mode;
+
+ if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.hw_mode = hapd->iface->conf->hw_mode;
+
+ /*
+ * If no chanlist config parameter is provided, include all enabled
+ * channels of the selected hw_mode.
+ */
+ if (hapd->iface->conf->acs_freq_list_present)
+ acs_ch_list_all = !hapd->iface->conf->acs_freq_list.num;
+ else
+ acs_ch_list_all = !hapd->iface->conf->acs_ch_list.num;
+
+ if (hapd->iface->current_mode)
+ selected_mode = hapd->iface->current_mode->mode;
+ else
+ selected_mode = HOSTAPD_MODE_IEEE80211ANY;
+
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+ if (selected_mode != HOSTAPD_MODE_IEEE80211ANY &&
+ selected_mode != mode->mode)
+ continue;
+ hostapd_get_hw_mode_any_channels(hapd, mode, acs_ch_list_all,
+ false, &freq_list);
+ }
+
+ if (!freq_list && hapd->iface->is_no_ir) {
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Interface freq_list is empty. Failing do_acs.");
+ return 1;
+ }
+
+ params.freq_list = freq_list;
+ params.edmg_enabled = hapd->iface->conf->enable_edmg;
+
+ params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+ params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+ params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+ params.eht_enabled = !!(hapd->iface->conf->ieee80211be);
+ params.ch_width = 20;
+ if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+ params.ch_width = 40;
+
+ /* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
+ */
+ if ((hapd->iface->conf->ieee80211be ||
+ hapd->iface->conf->ieee80211ax ||
+ hapd->iface->conf->ieee80211ac) &&
+ params.ht40_enabled) {
+ enum oper_chan_width oper_chwidth;
+
+ oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
+ if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+ params.ch_width = 80;
+ else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+ oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
+ params.ch_width = 160;
+ else if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+ params.ch_width = 320;
+ }
+
+ if (hapd->iface->conf->op_class)
+ params.ch_width = op_class_to_bandwidth(
+ hapd->iface->conf->op_class);
+ ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms);
+ os_free(freq_list);
+
+ return ret;
+}
+
+
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+ u16 reason_code, const u8 *ie, size_t ielen)
+{
+ if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
+ ie, ielen);
+}
+
+
+int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable)
+{
+ if (!hapd->driver || !hapd->driver->dpp_listen || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->dpp_listen(hapd->drv_priv, enable);
+}
+
+
+#ifdef CONFIG_PASN
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *peer_addr,
+ u32 cipher, u8 tk_len, const u8 *tk,
+ u8 ltf_keyseed_len,
+ const u8 *ltf_keyseed, u32 action)
+{
+ struct secure_ranging_params params;
+
+ if (!hapd->driver || !hapd->driver->set_secure_ranging_ctx)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.own_addr = own_addr;
+ params.peer_addr = peer_addr;
+ params.cipher = cipher;
+ params.tk_len = tk_len;
+ params.tk = tk;
+ params.ltf_keyseed_len = ltf_keyseed_len;
+ params.ltf_keyseed = ltf_keyseed;
+ params.action = action;
+
+ return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
+}
+#endif /* CONFIG_PASN */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.h
new file mode 100644
index 0000000..331b0ea
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.h
@@ -0,0 +1,457 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct hostapd_freq_params;
+
+u32 hostapd_sta_flags_to_drv(u32 flags);
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon,
+ struct wpabuf **proberesp,
+ struct wpabuf **assocresp);
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp);
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ const u8 *addr, int aid, int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ const struct ieee80211_vht_capabilities *vht_capab,
+ const struct ieee80211_he_capabilities *he_capab,
+ size_t he_capab_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+ u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+ int set, const u8 *link_addr, bool mld_link_sta);
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ size_t elem_len);
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname, const u8 *addr, void *bss_ctx,
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing);
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname);
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+ const u8 *addr, int idx, int link_id, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+ int freq, int channel, int edmg, u8 edmg_channel,
+ int ht_enabled, int vht_enabled, int he_enabled,
+ bool eht_enabled, int sec_channel_offset, int oper_chwidth,
+ int center_segment0, int center_segment1);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+ int total_flags, int flags_or, int flags_and);
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+ unsigned int weight);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+ u16 *flags, u8 *dfs_domain);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_drv_none(struct hostapd_data *hapd);
+bool hostapd_drv_nl80211(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+ struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+ struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+int hostapd_drv_set_key(const char *ifname,
+ struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int vlan_id, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len, enum key_flag key_flag);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack,
+ const u16 *csa_offs, size_t csa_offs_len,
+ int no_encrypt);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len);
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int wait, const u8 *dst,
+ const u8 *data, size_t len);
+static inline void
+hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
+{
+ if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
+ !hapd->drv_priv)
+ return;
+ hapd->driver->send_action_cancel_wait(hapd->drv_priv);
+}
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg);
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ enum hostapd_hw_mode mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int he_enabled, bool eht_enabled,
+ int sec_channel_offset, int oper_chwidth,
+ int center_segment0, int center_segment1,
+ bool radar_background);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+ u16 reason_code, const u8 *ie, size_t ielen);
+int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *addr,
+ u32 cipher, u8 key_len, const u8 *key,
+ u8 ltf_keyseed_len,
+ const u8 *ltf_keyseed, u32 action);
+
+
+#include "drivers/driver.h"
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+ enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len);
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
+void hostapd_get_ext_capa(struct hostapd_iface *iface);
+void hostapd_get_mld_capa(struct hostapd_iface *iface);
+
+void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode,
+ int acs_ch_list_all, bool allow_disabled,
+ int **freq_list);
+
+static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+ int enabled)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->hapd_set_countermeasures == NULL)
+ return 0;
+ return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+ struct hostapd_data *hapd,
+ const u8 *addr, int vlan_id,
+ int link_id)
+{
+ if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+ return 0;
+ return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+ vlan_id, link_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ return 0;
+ return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt,
+ u32 flags, int link_id)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+ return 0;
+ return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+ data_len, encrypt,
+ hapd->own_addr, flags, link_id);
+}
+
+static inline int hostapd_drv_read_sta_data(
+ struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+ return -1;
+ return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+ return 0;
+ return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
+ struct hostapd_acl_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
+ return 0;
+ return hapd->driver->set_acl(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
+ return 0;
+ return hapd->driver->set_ap(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+ const u8 *mac, int accepted,
+ u32 session_timeout)
+{
+ if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+ session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+ const u8 *mac)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_radius_acl_expire == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
+ int auth_algs)
+{
+ if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
+ return 0;
+ return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
+}
+
+static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
+ return;
+ hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
+}
+
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+ unsigned int freq)
+{
+ if (hapd->driver == NULL)
+ return -1;
+ if (!hapd->driver->get_survey)
+ return -1;
+ return hapd->driver->get_survey(hapd->drv_priv, freq);
+}
+
+static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+{
+ if (hapd->driver == NULL || hapd->driver->get_country == NULL)
+ return -1;
+ return hapd->driver->get_country(hapd->drv_priv, alpha2);
+}
+
+static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->get_radio_name == NULL)
+ return NULL;
+ return hapd->driver->get_radio_name(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
+ hapd->drv_priv == NULL)
+ return -1;
+
+ return hapd->driver->switch_channel(hapd->drv_priv, settings);
+}
+
+#ifdef CONFIG_IEEE80211AX
+static inline int hostapd_drv_switch_color(struct hostapd_data *hapd,
+ struct cca_settings *settings)
+{
+ if (!hapd->driver || !hapd->driver->switch_color || !hapd->drv_priv)
+ return -1;
+
+ return hapd->driver->switch_color(hapd->drv_priv, settings);
+}
+#endif /* CONFIG_IEEE80211AX */
+
+static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
+ return -1;
+ return hapd->driver->status(hapd->drv_priv, buf, buflen);
+}
+
+static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
+ int version, const u8 *ipaddr,
+ int prefixlen, const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_add_ip_neigh == NULL)
+ return -1;
+ return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+ prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+ u8 version, const u8 *ipaddr)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_delete_ip_neigh == NULL)
+ return -1;
+ return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+ ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+ enum drv_br_port_attr attr,
+ unsigned int val)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_port_set_attr == NULL)
+ return -1;
+ return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ enum drv_br_net_param param,
+ unsigned int val)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_set_net_param == NULL)
+ return -1;
+ return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+ int vendor_id, int subcmd,
+ const u8 *data, size_t data_len,
+ enum nested_attr nested_attr_flag,
+ struct wpabuf *buf)
+{
+ if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+ return -1;
+ return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+ data_len, nested_attr_flag, buf);
+}
+
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+ if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
+ return 0;
+ return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ struct wpa_channel_info *ci)
+{
+ if (!hapd->driver || !hapd->driver->channel_info)
+ return -1;
+ return hapd->driver->channel_info(hapd->drv_priv, ci);
+}
+
+static inline int
+hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
+ struct external_auth *params)
+{
+ if (!hapd->driver || !hapd->drv_priv ||
+ !hapd->driver->send_external_auth_status)
+ return -1;
+ return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
+}
+
+static inline int
+hostapd_drv_set_band(struct hostapd_data *hapd, u32 band_mask)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band)
+ return -1;
+ return hapd->driver->set_band(hapd->drv_priv, band_mask);
+}
+
+#ifdef ANDROID
+static inline int hostapd_drv_driver_cmd(struct hostapd_data *hapd,
+ char *cmd, char *buf, size_t buf_len)
+{
+ if (!hapd->driver->driver_cmd)
+ return -1;
+ return hapd->driver->driver_cmd(hapd->drv_priv, cmd, buf, buf_len);
+}
+#endif /* ANDROID */
+
+#ifdef CONFIG_TESTING_OPTIONS
+static inline int
+hostapd_drv_register_frame(struct hostapd_data *hapd, u16 type,
+ const u8 *match, size_t match_len,
+ bool multicast)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->register_frame)
+ return -1;
+ return hapd->driver->register_frame(hapd->drv_priv, type, match,
+ match_len, multicast);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+static inline int hostapd_drv_link_add(struct hostapd_data *hapd,
+ u8 link_id, const u8 *addr)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_add)
+ return -1;
+
+ return hapd->driver->link_add(hapd->drv_priv, link_id, addr);
+
+}
+#endif /* CONFIG_IEEE80211BE */
+
+#endif /* AP_DRV_OPS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.c
new file mode 100644
index 0000000..aa590d1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.c
@@ -0,0 +1,607 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ap_list.h"
+
+
+/* AP list is a double linked list with head->prev pointing to the end of the
+ * list and tail->next = NULL. Entries are moved to the head of the list
+ * whenever a beacon has been received from the AP in question. The tail entry
+ * in this link will thus be the least recently used entry. */
+
+
+static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ int i;
+
+ if (iface->current_mode == NULL ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+ iface->conf->channel != ap->channel)
+ return 0;
+
+ if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
+ return 1;
+
+ for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+ int rate = (ap->supported_rates[i] & 0x7f) * 5;
+ if (rate == 60 || rate == 90 || rate > 110)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
+{
+ struct ap_info *s;
+
+ s = iface->ap_hash[STA_HASH(ap)];
+ while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+ s = s->hnext;
+ return s;
+}
+
+
+static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ if (iface->ap_list) {
+ ap->prev = iface->ap_list->prev;
+ iface->ap_list->prev = ap;
+ } else
+ ap->prev = ap;
+ ap->next = iface->ap_list;
+ iface->ap_list = ap;
+}
+
+
+static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ if (iface->ap_list == ap)
+ iface->ap_list = ap->next;
+ else
+ ap->prev->next = ap->next;
+
+ if (ap->next)
+ ap->next->prev = ap->prev;
+ else if (iface->ap_list)
+ iface->ap_list->prev = ap->prev;
+}
+
+
+static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
+ iface->ap_hash[STA_HASH(ap->addr)] = ap;
+}
+
+
+static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ struct ap_info *s;
+
+ s = iface->ap_hash[STA_HASH(ap->addr)];
+ if (s == NULL) return;
+ if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+ iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
+ return;
+ }
+
+ while (s->hnext != NULL &&
+ os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+ s = s->hnext;
+ if (s->hnext != NULL)
+ s->hnext = s->hnext->hnext;
+ else
+ wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+ " from hash table", MAC2STR(ap->addr));
+}
+
+
+static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ ap_ap_hash_del(iface, ap);
+ ap_ap_list_del(iface, ap);
+
+ iface->num_ap--;
+ os_free(ap);
+}
+
+
+static void hostapd_free_aps(struct hostapd_iface *iface)
+{
+ struct ap_info *ap, *prev;
+
+ ap = iface->ap_list;
+
+ while (ap) {
+ prev = ap;
+ ap = ap->next;
+ ap_free_ap(iface, prev);
+ }
+
+ iface->ap_list = NULL;
+}
+
+
+static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
+{
+ struct ap_info *ap;
+
+ ap = os_zalloc(sizeof(struct ap_info));
+ if (ap == NULL)
+ return NULL;
+
+ /* initialize AP info data */
+ os_memcpy(ap->addr, addr, ETH_ALEN);
+ ap_ap_list_add(iface, ap);
+ iface->num_ap++;
+ ap_ap_hash_add(iface, ap);
+
+ if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
+ wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
+ MACSTR " from AP table", MAC2STR(ap->prev->addr));
+ ap_free_ap(iface, ap->prev);
+ }
+
+ return ap;
+}
+
+
+#ifdef CONFIG_24G_BW_SWITCH
+// To check 2.4G band 20M/40M bandwidth switch. Added by Yewei
+static int ap_list_handle_obss(struct hostapd_data *hapd, struct ieee802_11_elems *elems)
+{
+ int switch_to_20 = 0;
+ struct hostapd_iface *iface = hapd->iface;
+
+ // OBSS operation only applicable to 2.4G band.
+ if (hapd->iface->current_mode && (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A))
+ return 0;
+
+ // Only applicable when supporting at least HT 40
+ if ((!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) ||
+ (!(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)))
+ return 0;
+
+ // Only applicable when auto_20m_40m is enabled
+ if (!iface->conf->auto_20m_40m)
+ {
+ return 0;
+ }
+
+ // If the Beacon is on the same channel, no need to handle
+ if (elems->ds_params && hapd->iface->current_mode &&
+ (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+ (hapd->iconf->channel == elems->ds_params[0])) {
+ return 0;
+ }
+
+ // Check if need to switch to 20M bandwidth
+ if (elems->ht_capabilities) {
+ struct ieee80211_ht_capabilities *ht_cap = (struct ieee80211_ht_capabilities *)elems->ht_capabilities;
+ if (0 != iface->conf->secondary_channel) {
+ iface->secondary_ch = iface->conf->secondary_channel;
+ iface->conf->secondary_channel = 0;
+ //ieee802_11_update_beacons(iface);
+ switch_to_20 = 1;
+ wpa_printf(MSG_INFO, "ap_list_handle_obss, %s, switch_to_20:%d", iface->phy, switch_to_20);
+ }
+ }
+ else if (!elems->ht_capabilities && !elems->vht_capabilities && !elems->he_capabilities) {
+ if (iface->conf->secondary_channel) {
+ iface->secondary_ch = iface->conf->secondary_channel;
+ iface->conf->secondary_channel = 0;
+ //ieee802_11_update_beacons(iface);
+ switch_to_20 = 1;
+ wpa_printf(MSG_INFO, "ap_list_handle_obss, %s, switch_to_20:%d", iface->phy, switch_to_20);
+ }
+ }
+
+ return switch_to_20;
+}
+#endif /* CONFIG_24G_BW_SWITCH */
+
+
+#ifdef CONFIG_5G_BW_SWITCH
+// To check 5G band 20M/40M/80M bandwidth switch in AP list timer. Added by Liangyu Chu
+static int ap_list_5g_BW_switch(struct hostapd_iface *iface)
+{
+ // Only applicable to 5G band
+ if (iface->current_mode &&
+ (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+ iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B))
+ {
+ /*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, Not at 5G band", iface->phy);*/
+ return 0;
+ }
+
+ // Only applicable when supporting at least HT 40
+ if (!iface->conf->ht_capab || !(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ {
+ /*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, Not support HT 40M bandwidth", iface->phy);*/
+ return 0;
+ }
+
+ // Only applicable to VHT or HE mode
+ // if (!iface->conf->vht_capab || !iface->conf->he_phy_capab)
+ if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+ {
+ /*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, Not support VHT or HE capabilities", iface->phy);*/
+ return 0;
+ }
+
+ // Only applicable when auto_80M is enabled
+ if (!iface->conf->auto_80m)
+ {
+ /*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, auto BW switch is not enabled", iface->phy);*/
+ return 0;
+ }
+
+ int bw_switch = 0;
+ int current_bw = 20;
+ int target_bw = 80;
+
+ // Compute current bandwith
+ if (iface->conf->vht_oper_chwidth
+#ifdef CONFIG_IEEE80211AX
+ || iface->conf->he_oper_chwidth
+#endif
+ )
+ current_bw = 80;
+ else if (iface->conf->secondary_channel)
+ current_bw = 40;
+
+ // Compute target bandwidth from AP list statistics
+ if ((iface->num_ap > AP_LIST_MAX_NUM_STA_TO_20M) ||
+ (iface->max_rssi && (iface->max_rssi > AP_LIST_MAX_RSSI_TO_20M)))
+ target_bw = 20;
+ else if ((iface->num_ap > AP_LIST_MAX_NUM_STA_TO_40M) ||
+ (iface->max_rssi && (iface->max_rssi > AP_LIST_MAX_RSSI_TO_40M)))
+ target_bw = 40;
+
+ // Only applicable when BW changes
+ if (current_bw == target_bw)
+ {
+ wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, num_ap=%d, max_rssi=%d, target_bw=%d, current_bw=%d, no need to switch BW",
+ iface->phy, iface->num_ap, iface->max_rssi, target_bw, current_bw);
+ return 0;
+ }
+ else
+ {
+ wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, num_ap=%d, max_rssi=%d, target_bw=%d, current_bw=%d, need to switch BW",
+ iface->phy, iface->num_ap, iface->max_rssi, target_bw, current_bw);
+ }
+
+ // 80M -> 20M switch
+ if (current_bw == 80 && target_bw == 20)
+ {
+ // Record previous settings
+ iface->secondary_ch = iface->conf->secondary_channel;
+ iface->vht_oper_ch_bw = iface->conf->vht_oper_chwidth;
+ iface->vht_center_freq_seg0_80m = iface->conf->vht_oper_centr_freq_seg0_idx;
+
+ // 20M bandwidth configuration
+ iface->conf->secondary_channel = 0;
+ iface->conf->vht_oper_chwidth = 0;
+ iface->conf->vht_oper_centr_freq_seg0_idx = 0;
+
+ bw_switch = 1;
+ }
+ // 20M -> 80M switch
+ else if (current_bw == 20 && target_bw == 80)
+ {
+ // 80M bandwidth configuration
+ iface->conf->secondary_channel = iface->secondary_ch;
+ iface->conf->vht_oper_chwidth = iface->vht_oper_ch_bw;
+ iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_80m;
+
+ bw_switch = 1;
+ }
+
+ // 80M -> 40M switch
+ else if (current_bw == 80 && target_bw == 40)
+ {
+ // Record previous settings
+ iface->vht_oper_ch_bw = iface->conf->vht_oper_chwidth;
+ iface->vht_center_freq_seg0_80m = iface->conf->vht_oper_centr_freq_seg0_idx;
+
+ // 40M bandwidth configuration
+ iface->conf->vht_oper_chwidth = 0;
+ iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_40m;
+
+ bw_switch = 1;
+ }
+ // 40M -> 80M switch
+ else if (current_bw == 40 && target_bw == 80)
+ {
+ // Record previous settings
+ iface->vht_center_freq_seg0_40m = iface->conf->vht_oper_centr_freq_seg0_idx;
+
+ // 80M bandwidth configuration
+ iface->conf->vht_oper_chwidth = iface->vht_oper_ch_bw;
+ iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_80m;
+
+ bw_switch = 1;
+ }
+
+ // 40M -> 20M switch
+ else if (current_bw == 40 && target_bw == 20)
+ {
+ // Record previous settings
+ iface->secondary_ch = iface->conf->secondary_channel;
+ iface->vht_center_freq_seg0_40m = iface->conf->vht_oper_centr_freq_seg0_idx;
+
+ // 20M bandwidth configuration
+ iface->conf->secondary_channel = 0;
+ iface->conf->vht_oper_centr_freq_seg0_idx = 0;
+
+ bw_switch = 1;
+ }
+ // 20M -> 40M switch
+ else if (current_bw == 20 && target_bw == 40)
+ {
+ // 40M bandwidth configuration
+ iface->conf->secondary_channel = iface->secondary_ch;
+ iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_40m;
+
+ bw_switch = 1;
+ }
+
+ // Always set HE operation IEs the same as VHT operation IEs if applicable
+#ifdef CONFIG_IEEE80211AX
+ if (bw_switch && iface->conf->ieee80211ac && iface->conf->ieee80211ax)
+ {
+ iface->conf->he_oper_chwidth = iface->conf->vht_oper_chwidth;
+ iface->conf->he_oper_centr_freq_seg0_idx = iface->conf->vht_oper_centr_freq_seg0_idx;
+ }
+#endif
+
+ return bw_switch;
+}
+#endif /* CONFIG_5G_BW_SWITCH */
+
+
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+void ap_list_process_beacon(struct hostapd_data *hapd,
+#else
+void ap_list_process_beacon(struct hostapd_iface *iface,
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+ const struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct hostapd_frame_info *fi)
+{
+ struct ap_info *ap;
+ int new_ap = 0;
+ int set_beacon = 0;
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+ struct hostapd_iface *iface = hapd->iface;
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+#ifdef CONFIG_5G_BW_SWITCH
+ int ssi_signal = fi ? fi->ssi_signal : 0;
+ int ch_width = 20; // Default set each AP as 20M bandwidth
+#endif /* CONFIG_5G_BW_SWITCH */
+
+ if (iface->conf->ap_table_max_size < 1)
+ return;
+
+ ap = ap_get_ap(iface, mgmt->bssid);
+ if (!ap) {
+ ap = ap_ap_add(iface, mgmt->bssid);
+ if (!ap) {
+ wpa_printf(MSG_INFO,
+ "Failed to allocate AP information entry");
+ return;
+ }
+ new_ap = 1;
+ }
+
+ merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
+ elems->supp_rates, elems->supp_rates_len,
+ elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+ // Check ERP related info
+ if (elems->erp_info)
+ ap->erp = elems->erp_info[0];
+ else
+ ap->erp = -1;
+
+ // Check channel info
+ if (elems->ds_params)
+ ap->channel = elems->ds_params[0];
+ else if (elems->ht_operation)
+ ap->channel = elems->ht_operation[0];
+ else if (fi)
+ ap->channel = fi->channel;
+
+ // Check HT capablities
+ if (elems->ht_capabilities)
+ ap->ht_support = 1;
+ else
+ ap->ht_support = 0;
+
+#ifdef CONFIG_5G_BW_SWITCH
+ // Check VHT capabilities
+ if (elems->vht_capabilities)
+ ap->vht_support = 1;
+ else
+ ap->vht_support = 0;
+
+ // Check HE capabilities
+ if (elems->he_capabilities)
+ ap->he_support = 1;
+ else
+ ap->he_support = 0;
+
+ // Check operation bandwidth as reported in Beacon frame. Only consider VHT and HE.
+ if ((ap->vht_support || ap->he_support) && (elems->vht_operation))
+ {
+ struct ieee80211_vht_operation *vht_op = (struct ieee80211_vht_operation *)elems->vht_operation;
+ int op_bw = vht_op->vht_op_info_chwidth;
+ switch (op_bw)
+ {
+ case 0: ch_width = 20; break;
+ case 1: ch_width = 80; break;
+ case 2: ch_width = 160; break;
+ default: break;
+ }
+ }
+ ap->ch_width = ch_width;
+
+ // Check RSSI of the received Beacon frame
+ ap->ssi_signal = ssi_signal;
+ if ((iface->max_rssi == 0) || (ssi_signal > iface->max_rssi))
+ iface->max_rssi = ssi_signal;
+
+ /*wpa_printf(MSG_INFO, "ap_list_process_beacon: BSSID=" MACSTR ", channel=%d, ch_width=%d, ssi_signal=%d, ht_support=%d, vht_support=%d, he_support=%d",
+ MAC2STR(mgmt->bssid), ap->channel, ap->ch_width, ap->ssi_signal, ap->ht_support, ap->vht_support, ap->he_support);*/
+#endif /* CONFIG_5G_BW_SWITCH */
+
+ os_get_reltime(&ap->last_beacon);
+
+ if (!new_ap && ap != iface->ap_list) {
+ /* move AP entry into the beginning of the list so that the
+ * oldest entry is always in the end of the list */
+ ap_ap_list_del(iface, ap);
+ ap_ap_list_add(iface, ap);
+ }
+
+ if (!iface->olbc &&
+ ap_list_beacon_olbc(iface, ap)) {
+ iface->olbc = 1;
+ wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
+ " (channel %d) - enable protection",
+ MAC2STR(ap->addr), ap->channel);
+ set_beacon++;
+ }
+
+ if (!iface->olbc_ht && !ap->ht_support &&
+ (ap->channel == 0 ||
+ ap->channel == iface->conf->channel ||
+ ap->channel == iface->conf->channel +
+ iface->conf->secondary_channel * 4)) {
+ iface->olbc_ht = 1;
+ hostapd_ht_operation_update(iface);
+ wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
+ " (channel %d) - enable protection",
+ MAC2STR(ap->addr), ap->channel);
+ set_beacon++;
+ }
+
+#ifdef CONFIG_24G_BW_SWITCH
+ // 2.4G band 20M/40M bandwidth adaptation. Added by Yewei
+ if (ap_list_handle_obss(hapd, elems))
+ set_beacon++;
+#endif /* CONFIG_24G_BW_SWITCH */
+#if 0
+ // 5G band 20M/40M/80M bandwidth adaptation. Added by Liangyu Chu
+ if (ap_list_5g_BW_switch(iface))
+ set_beacon++;
+#endif
+
+ if (set_beacon)
+ ieee802_11_update_beacons(iface);
+}
+
+
+void ap_list_timer(struct hostapd_iface *iface)
+{
+ struct os_reltime now;
+ struct ap_info *ap;
+ int set_beacon = 0;
+
+ if (!iface->ap_list)
+ return;
+
+ os_get_reltime(&now);
+
+ while (iface->ap_list) {
+ ap = iface->ap_list->prev;
+ if (!os_reltime_expired(&now, &ap->last_beacon,
+ iface->conf->ap_table_expiration_time))
+ break;
+
+ ap_free_ap(iface, ap);
+ }
+
+#ifdef CONFIG_24G_BW_SWITCH
+ /* yewei, if the number of neighbor aps is zero, need swtich to 40Mhz, 2024-01-04 */
+ ap = iface->ap_list;
+ if ((!ap) && (iface->secondary_ch) && (0 == iface->conf->secondary_channel) &&
+ (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) &&
+ (iface->conf->auto_20m_40m)) {
+ iface->conf->secondary_channel = iface->secondary_ch;
+ set_beacon++;
+ wpa_printf(MSG_INFO, "ap_list_timer, %s, switch_to_40", iface->phy);
+ }
+#endif /* CONFIG_24G_BW_SWITCH */
+
+#ifdef CONFIG_5G_BW_SWITCH
+ // 5G band 20M/40M/80M bandwidth adaptation. Added by Liangyu Chu
+ if (ap_list_5g_BW_switch(iface))
+ set_beacon++;
+
+ // Update max RSSI level
+ iface->max_rssi = 0;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+ if (iface->olbc || iface->olbc_ht) {
+ int olbc = 0;
+ int olbc_ht = 0;
+
+ ap = iface->ap_list;
+ while (ap && (olbc == 0 || olbc_ht == 0)) {
+ if (ap_list_beacon_olbc(iface, ap))
+ olbc = 1;
+ if (!ap->ht_support)
+ olbc_ht = 1;
+ ap = ap->next;
+ }
+ if (!olbc && iface->olbc) {
+ wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
+ iface->olbc = 0;
+ set_beacon++;
+ }
+ if (!olbc_ht && iface->olbc_ht) {
+ wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
+ iface->olbc_ht = 0;
+ hostapd_ht_operation_update(iface);
+ set_beacon++;
+ }
+ }
+
+ if (set_beacon)
+ ieee802_11_update_beacons(iface);
+}
+
+
+int ap_list_init(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+
+void ap_list_deinit(struct hostapd_iface *iface)
+{
+ hostapd_free_aps(iface);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.h
new file mode 100644
index 0000000..02630b7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.h
@@ -0,0 +1,77 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_LIST_H
+#define AP_LIST_H
+
+#ifdef CONFIG_5G_BW_SWITCH
+#define AP_LIST_MAX_NUM_STA_TO_20M 12
+#define AP_LIST_MAX_NUM_STA_TO_40M 6
+#define AP_LIST_MAX_RSSI_TO_20M -30
+#define AP_LIST_MAX_RSSI_TO_40M -50
+#endif /* CONFIG_5G_BW_SWITCH */
+
+struct ap_info {
+ /* Note: next/prev pointers are updated whenever a new beacon is
+ * received because these are used to find the least recently used
+ * entries. */
+ struct ap_info *next; /* next entry in AP list */
+ struct ap_info *prev; /* previous entry in AP list */
+ struct ap_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+ int erp; /* ERP Info or -1 if ERP info element not present */
+
+ // Add more AP Info for bandwidth adaptation. Added by Liangyu Chu.
+ int channel;
+#ifdef CONFIG_5G_BW_SWITCH
+ int ssi_signal;
+ int ch_width;
+#endif /* CONFIG_5G_BW_SWITCH */
+ int ht_support;
+#ifdef CONFIG_5G_BW_SWITCH
+ int vht_support;
+ int he_support;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+ struct os_reltime last_beacon;
+};
+
+struct ieee802_11_elems;
+struct hostapd_frame_info;
+
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+void ap_list_process_beacon(struct hostapd_data *hapd,
+#else
+void ap_list_process_beacon(struct hostapd_iface *iface,
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+ const struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct hostapd_frame_info *fi);
+#ifdef NEED_AP_MLME
+int ap_list_init(struct hostapd_iface *iface);
+void ap_list_deinit(struct hostapd_iface *iface);
+void ap_list_timer(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline int ap_list_init(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+static inline void ap_list_deinit(struct hostapd_iface *iface)
+{
+}
+
+static inline void ap_list_timer(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* AP_LIST_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.c
new file mode 100644
index 0000000..309e69a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "hostapd.h"
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static const char * mlme_auth_alg_str(int alg)
+{
+ switch (alg) {
+ case WLAN_AUTH_OPEN:
+ return "OPEN_SYSTEM";
+ case WLAN_AUTH_SHARED_KEY:
+ return "SHARED_KEY";
+ case WLAN_AUTH_FT:
+ return "FT";
+ default:
+ return "unknown";
+ }
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * mlme_authenticate_indication - Report the establishment of an authentication
+ * relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * authentication relationship with a specific peer MAC entity that
+ * resulted from an authentication procedure that was initiated by
+ * that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
+ */
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
+ MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK &&
+ !(sta->flags & WLAN_STA_MFP))
+ mlme_deletekeys_request(hapd, sta);
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+}
+
+
+/**
+ * mlme_deauthenticate_indication - Report the invalidation of an
+ * authentication relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Deauthentication frame
+ *
+ * MLME calls this function as a result of the invalidation of an
+ * authentication relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
+ MAC2STR(sta->addr), reason_code);
+ if (!hapd->iface->driver_ap_teardown)
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_associate_indication - Report the establishment of an association with
+ * a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * association with a specific peer MAC entity that resulted from an
+ * association procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-ASSOCIATE.indication(" MACSTR ")",
+ MAC2STR(sta->addr));
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK)
+ mlme_deletekeys_request(hapd, sta);
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+}
+
+
+/**
+ * mlme_reassociate_indication - Report the establishment of an reassociation
+ * with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * reassociation with a specific peer MAC entity that resulted from a
+ * reassociation procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-REASSOCIATE.indication(" MACSTR ")",
+ MAC2STR(sta->addr));
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK)
+ mlme_deletekeys_request(hapd, sta);
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+}
+
+
+/**
+ * mlme_disassociate_indication - Report disassociation with a specific peer
+ * MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Disassociation frame
+ *
+ * MLME calls this function as a result of the invalidation of an association
+ * relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
+ MAC2STR(sta->addr), reason_code);
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-MichaelMICFailure.indication(" MACSTR ")",
+ MAC2STR(addr));
+}
+
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-DELETEKEYS.request(" MACSTR ")",
+ MAC2STR(sta->addr));
+
+ if (sta->wpa_sm)
+ wpa_remove_ptk(sta->wpa_sm);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.h
new file mode 100644
index 0000000..e7fd69d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.h
@@ -0,0 +1,34 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MLME_H
+#define MLME_H
+
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code);
+
+void mlme_associate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code);
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+ const u8 *addr);
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* MLME_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.c
new file mode 100644
index 0000000..cc1d722
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.c
@@ -0,0 +1,418 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "eap_server/eap_sim_db.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_server.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "authsrv.h"
+
+
+#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
+#define EAP_SIM_DB
+#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
+
+
+#ifdef EAP_SIM_DB
+static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
+ return 1;
+ return 0;
+}
+
+
+static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
+#ifdef RADIUS_SERVER
+ radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
+#endif /* RADIUS_SERVER */
+ }
+}
+#endif /* EAP_SIM_DB */
+
+
+#ifdef RADIUS_SERVER
+
+static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ const struct hostapd_eap_user *eap_user;
+ int i;
+ int rv = -1;
+
+ eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
+ if (eap_user == NULL)
+ goto out;
+
+ if (user == NULL)
+ return 0;
+
+ os_memset(user, 0, sizeof(*user));
+ for (i = 0; i < EAP_MAX_METHODS; i++) {
+ user->methods[i].vendor = eap_user->methods[i].vendor;
+ user->methods[i].method = eap_user->methods[i].method;
+ }
+
+ if (eap_user->password) {
+ user->password = os_memdup(eap_user->password,
+ eap_user->password_len);
+ if (user->password == NULL)
+ goto out;
+ user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
+ }
+ user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
+ user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
+ user->accept_attr = eap_user->accept_attr;
+ user->t_c_timestamp = eap_user->t_c_timestamp;
+ rv = 0;
+
+out:
+ if (rv)
+ wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+ return rv;
+}
+
+
+static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
+{
+ struct radius_server_conf srv;
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using RADIUS server of the first BSS");
+
+ hapd->radius_srv = hapd->mld_first_bss->radius_srv;
+ return 0;
+ }
+
+ os_memset(&srv, 0, sizeof(srv));
+ srv.client_file = conf->radius_server_clients;
+ srv.auth_port = conf->radius_server_auth_port;
+ srv.acct_port = conf->radius_server_acct_port;
+ srv.conf_ctx = hapd;
+ srv.ipv6 = conf->radius_server_ipv6;
+ srv.get_eap_user = hostapd_radius_get_eap_user;
+ srv.eap_req_id_text = conf->eap_req_id_text;
+ srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+ srv.sqlite_file = conf->eap_user_sqlite;
+#ifdef CONFIG_RADIUS_TEST
+ srv.dump_msk_file = conf->dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+ srv.subscr_remediation_url = conf->subscr_remediation_url;
+ srv.subscr_remediation_method = conf->subscr_remediation_method;
+ srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
+ srv.t_c_server_url = conf->t_c_server_url;
+#endif /* CONFIG_HS20 */
+ srv.erp_domain = conf->erp_domain;
+ srv.eap_cfg = hapd->eap_cfg;
+
+ hapd->radius_srv = radius_server_init(&srv);
+ if (hapd->radius_srv == NULL) {
+ wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* RADIUS_SERVER */
+
+
+#ifdef EAP_TLS_FUNCS
+static void authsrv_tls_event(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ switch (ev) {
+ case TLS_CERT_CHAIN_SUCCESS:
+ wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
+ break;
+ case TLS_CERT_CHAIN_FAILURE:
+ wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+ data->cert_fail.reason,
+ data->cert_fail.depth,
+ data->cert_fail.subject,
+ data->cert_fail.reason_txt);
+ break;
+ case TLS_PEER_CERTIFICATE:
+ wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
+ data->peer_cert.depth,
+ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+ data->peer_cert.subject);
+ break;
+ case TLS_ALERT:
+ if (data->alert.is_local)
+ wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
+ data->alert.description);
+ else
+ wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
+ data->alert.description);
+ break;
+ case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+ /* Not applicable to TLS server */
+ break;
+ }
+}
+#endif /* EAP_TLS_FUNCS */
+
+
+static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd)
+{
+ struct eap_config *cfg;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (!cfg)
+ return NULL;
+
+ cfg->eap_server = hapd->conf->eap_server;
+ cfg->ssl_ctx = hapd->ssl_ctx;
+ cfg->msg_ctx = hapd->msg_ctx;
+ cfg->eap_sim_db_priv = hapd->eap_sim_db_priv;
+ cfg->tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ cfg->tls_flags = hapd->conf->tls_flags;
+ cfg->max_auth_rounds = hapd->conf->max_auth_rounds;
+ cfg->max_auth_rounds_short = hapd->conf->max_auth_rounds_short;
+ if (hapd->conf->pac_opaque_encr_key)
+ cfg->pac_opaque_encr_key =
+ os_memdup(hapd->conf->pac_opaque_encr_key, 16);
+ if (hapd->conf->eap_fast_a_id) {
+ cfg->eap_fast_a_id = os_memdup(hapd->conf->eap_fast_a_id,
+ hapd->conf->eap_fast_a_id_len);
+ cfg->eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+ }
+ if (hapd->conf->eap_fast_a_id_info)
+ cfg->eap_fast_a_id_info =
+ os_strdup(hapd->conf->eap_fast_a_id_info);
+ cfg->eap_fast_prov = hapd->conf->eap_fast_prov;
+ cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime;
+ cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+ cfg->eap_teap_auth = hapd->conf->eap_teap_auth;
+ cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
+ cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
+ cfg->eap_teap_id = hapd->conf->eap_teap_id;
+ cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
+ cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
+ cfg->eap_sim_id = hapd->conf->eap_sim_id;
+ cfg->imsi_privacy_key = hapd->imsi_privacy_key;
+ cfg->tnc = hapd->conf->tnc;
+ cfg->wps = hapd->wps;
+ cfg->fragment_size = hapd->conf->fragment_size;
+ cfg->pwd_group = hapd->conf->pwd_group;
+ cfg->pbc_in_m1 = hapd->conf->pbc_in_m1;
+ if (hapd->conf->server_id) {
+ cfg->server_id = (u8 *) os_strdup(hapd->conf->server_id);
+ cfg->server_id_len = os_strlen(hapd->conf->server_id);
+ } else {
+ cfg->server_id = (u8 *) os_strdup("hostapd");
+ cfg->server_id_len = 7;
+ }
+ cfg->erp = hapd->conf->eap_server_erp;
+#ifdef CONFIG_TESTING_OPTIONS
+ cfg->skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return cfg;
+}
+
+
+int authsrv_init(struct hostapd_data *hapd)
+{
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
+
+#ifdef EAP_TLS_FUNCS
+ hapd->ssl_ctx = hapd->mld_first_bss->ssl_ctx;
+#endif /* EAP_TLS_FUNCS */
+ hapd->eap_cfg = hapd->mld_first_bss->eap_cfg;
+#ifdef EAP_SIM_DB
+ hapd->eap_sim_db_priv = hapd->mld_first_bss->eap_sim_db_priv;
+#endif /* EAP_SIM_DB */
+ return 0;
+ }
+
+#ifdef EAP_TLS_FUNCS
+ if (hapd->conf->eap_server &&
+ (hapd->conf->ca_cert || hapd->conf->server_cert ||
+ hapd->conf->private_key || hapd->conf->dh_file ||
+ hapd->conf->server_cert2 || hapd->conf->private_key2)) {
+ struct tls_config conf;
+ struct tls_connection_params params;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ if (hapd->conf->crl_reload_interval > 0 &&
+ hapd->conf->check_crl <= 0) {
+ wpa_printf(MSG_INFO,
+ "Cannot enable CRL reload functionality - it depends on check_crl being set");
+ } else if (hapd->conf->crl_reload_interval > 0) {
+ conf.crl_reload_interval =
+ hapd->conf->crl_reload_interval;
+ wpa_printf(MSG_INFO,
+ "Enabled CRL reload functionality");
+ }
+ conf.tls_flags = hapd->conf->tls_flags;
+ conf.event_cb = authsrv_tls_event;
+ conf.cb_ctx = hapd;
+ hapd->ssl_ctx = tls_init(&conf);
+ if (hapd->ssl_ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize TLS");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.ca_cert = hapd->conf->ca_cert;
+ params.client_cert = hapd->conf->server_cert;
+ params.client_cert2 = hapd->conf->server_cert2;
+ params.private_key = hapd->conf->private_key;
+ params.private_key2 = hapd->conf->private_key2;
+ params.private_key_passwd = hapd->conf->private_key_passwd;
+ params.private_key_passwd2 = hapd->conf->private_key_passwd2;
+ params.dh_file = hapd->conf->dh_file;
+ params.openssl_ciphers = hapd->conf->openssl_ciphers;
+ params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
+ params.ocsp_stapling_response =
+ hapd->conf->ocsp_stapling_response;
+ params.ocsp_stapling_response_multi =
+ hapd->conf->ocsp_stapling_response_multi;
+ params.check_cert_subject = hapd->conf->check_cert_subject;
+
+ if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) {
+ wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+
+ if (tls_global_set_verify(hapd->ssl_ctx,
+ hapd->conf->check_crl,
+ hapd->conf->check_crl_strict)) {
+ wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+ }
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef CRYPTO_RSA_OAEP_SHA256
+ crypto_rsa_key_free(hapd->imsi_privacy_key);
+ hapd->imsi_privacy_key = NULL;
+ if (hapd->conf->imsi_privacy_key) {
+ hapd->imsi_privacy_key = crypto_rsa_key_read(
+ hapd->conf->imsi_privacy_key, true);
+ if (!hapd->imsi_privacy_key) {
+ wpa_printf(MSG_ERROR,
+ "Failed to read/parse IMSI privacy key %s",
+ hapd->conf->imsi_privacy_key);
+ authsrv_deinit(hapd);
+ return -1;
+ }
+ }
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
+#ifdef EAP_SIM_DB
+ if (hapd->conf->eap_sim_db) {
+ hapd->eap_sim_db_priv =
+ eap_sim_db_init(hapd->conf->eap_sim_db,
+ hapd->conf->eap_sim_db_timeout,
+ hostapd_sim_db_cb, hapd);
+ if (hapd->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
+ "database interface");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+ }
+#endif /* EAP_SIM_DB */
+
+ hapd->eap_cfg = authsrv_eap_config(hapd);
+ if (!hapd->eap_cfg) {
+ wpa_printf(MSG_ERROR,
+ "Failed to build EAP server configuration");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+
+#ifdef RADIUS_SERVER
+ if (hapd->conf->radius_server_clients &&
+ hostapd_setup_radius_srv(hapd))
+ return -1;
+#endif /* RADIUS_SERVER */
+
+ return 0;
+}
+
+
+void authsrv_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Deinit auth_serv of a non-first BSS");
+
+ hapd->radius_srv = NULL;
+ hapd->eap_cfg = NULL;
+#ifdef EAP_SIM_DB
+ hapd->eap_sim_db_priv = NULL;
+#endif /* EAP_SIM_DB */
+#ifdef EAP_TLS_FUNCS
+ hapd->ssl_ctx = NULL;
+#endif /* EAP_TLS_FUNCS */
+ return;
+ }
+
+#ifdef RADIUS_SERVER
+ radius_server_deinit(hapd->radius_srv);
+ hapd->radius_srv = NULL;
+#endif /* RADIUS_SERVER */
+
+#ifdef CRYPTO_RSA_OAEP_SHA256
+ crypto_rsa_key_free(hapd->imsi_privacy_key);
+ hapd->imsi_privacy_key = NULL;
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
+#ifdef EAP_TLS_FUNCS
+ if (hapd->ssl_ctx) {
+ tls_deinit(hapd->ssl_ctx);
+ hapd->ssl_ctx = NULL;
+ }
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+ if (hapd->eap_sim_db_priv) {
+ eap_sim_db_deinit(hapd->eap_sim_db_priv);
+ hapd->eap_sim_db_priv = NULL;
+ }
+#endif /* EAP_SIM_DB */
+
+ eap_server_config_free(hapd->eap_cfg);
+ hapd->eap_cfg = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.h
new file mode 100644
index 0000000..2f4ed34
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.h
@@ -0,0 +1,15 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AUTHSRV_H
+#define AUTHSRV_H
+
+int authsrv_init(struct hostapd_data *hapd);
+void authsrv_deinit(struct hostapd_data *hapd);
+
+#endif /* AUTHSRV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.c
new file mode 100644
index 0000000..78b3476
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.c
@@ -0,0 +1,2285 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "hs20.h"
+#include "dfs.h"
+#include "taxonomy.h"
+#include "ieee802_11_auth.h"
+
+
+#ifdef NEED_AP_MLME
+
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+ if (len < 2 + 5)
+ return eid;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->bss_load_test_set) {
+ *eid++ = WLAN_EID_BSS_LOAD;
+ *eid++ = 5;
+ os_memcpy(eid, hapd->conf->bss_load_test, 5);
+ eid += 5;
+ return eid;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (hapd->conf->bss_load_update_period) {
+ *eid++ = WLAN_EID_BSS_LOAD;
+ *eid++ = 5;
+ WPA_PUT_LE16(eid, hapd->num_sta);
+ eid += 2;
+ *eid++ = hapd->iface->channel_utilization;
+ WPA_PUT_LE16(eid, 0); /* no available admission capabity */
+ eid += 2;
+ }
+ return eid;
+}
+
+
+static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
+{
+ u8 erp = 0;
+
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return 0;
+
+ if (hapd->iface->olbc)
+ erp |= ERP_INFO_USE_PROTECTION;
+ if (hapd->iface->num_sta_non_erp > 0) {
+ erp |= ERP_INFO_NON_ERP_PRESENT |
+ ERP_INFO_USE_PROTECTION;
+ }
+ if (hapd->iface->num_sta_no_short_preamble > 0 ||
+ hapd->iconf->preamble == LONG_PREAMBLE)
+ erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
+
+ return erp;
+}
+
+
+static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
+{
+ *eid++ = WLAN_EID_DS_PARAMS;
+ *eid++ = 1;
+ *eid++ = hapd->iconf->channel;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
+{
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return eid;
+
+ /* Set NonERP_present and use_protection bits if there
+ * are any associated NonERP stations. */
+ /* TODO: use_protection bit can be set to zero even if
+ * there are NonERP stations present. This optimization
+ * might be useful if NonERP stations are "quiet".
+ * See 802.11g/D6 E-1 for recommended practice.
+ * In addition, Non ERP present might be set, if AP detects Non ERP
+ * operation on other APs. */
+
+ /* Add ERP Information element */
+ *eid++ = WLAN_EID_ERP_INFO;
+ *eid++ = 1;
+ *eid++ = ieee802_11_erp_info(hapd);
+
+ return eid;
+}
+
+
+static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 local_pwr_constraint = 0;
+ int dfs;
+
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ /* Let host drivers add this IE if DFS support is offloaded */
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return eid;
+
+ /*
+ * There is no DFS support and power constraint was not directly
+ * requested by config option.
+ */
+ if (!hapd->iconf->ieee80211h &&
+ hapd->iconf->local_pwr_constraint == -1)
+ return eid;
+
+ /* Check if DFS is required by regulatory. */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0) {
+ wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+ dfs);
+ dfs = 0;
+ }
+
+ if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
+ return eid;
+
+ /*
+ * ieee80211h (DFS) is enabled so Power Constraint element shall
+ * be added when running on DFS channel whenever local_pwr_constraint
+ * is configured or not. In order to meet regulations when TPC is not
+ * implemented using a transmit power that is below the legal maximum
+ * (including any mitigation factor) should help. In this case,
+ * indicate 3 dB below maximum allowed transmit power.
+ */
+ if (hapd->iconf->local_pwr_constraint == -1)
+ local_pwr_constraint = 3;
+
+ /*
+ * A STA that is not an AP shall use a transmit power less than or
+ * equal to the local maximum transmit power level for the channel.
+ * The local maximum transmit power can be calculated from the formula:
+ * local max TX pwr = max TX pwr - local pwr constraint
+ * Where max TX pwr is maximum transmit power level specified for
+ * channel in Country element and local pwr constraint is specified
+ * for channel in this Power Constraint element.
+ */
+
+ /* Element ID */
+ *pos++ = WLAN_EID_PWR_CONSTRAINT;
+ /* Length */
+ *pos++ = 1;
+ /* Local Power Constraint */
+ if (local_pwr_constraint)
+ *pos++ = local_pwr_constraint;
+ else
+ *pos++ = hapd->iconf->local_pwr_constraint;
+
+ return pos;
+}
+
+
+static u8 * hostapd_eid_country_add(struct hostapd_data *hapd, u8 *pos,
+ u8 *end, int chan_spacing,
+ struct hostapd_channel_data *start,
+ struct hostapd_channel_data *prev)
+{
+ if (end - pos < 3)
+ return pos;
+
+ /* first channel number */
+ *pos++ = start->chan;
+ /* number of channels */
+ *pos++ = (prev->chan - start->chan) / chan_spacing + 1;
+ /* maximum transmit power level */
+ if (!is_6ghz_op_class(hapd->iconf->op_class))
+ *pos++ = start->max_tx_power;
+ else
+ *pos++ = 0; /* Reserved when operating on the 6 GHz band */
+
+ return pos;
+}
+
+
+static u8 * hostapd_fill_subband_triplets(struct hostapd_data *hapd, u8 *pos,
+ u8 *end)
+{
+ int i;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *start, *prev;
+ int chan_spacing = 1;
+
+ mode = hapd->iface->current_mode;
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A)
+ chan_spacing = 4;
+
+ start = prev = NULL;
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (start && prev &&
+ prev->chan + chan_spacing == chan->chan &&
+ start->max_tx_power == chan->max_tx_power) {
+ prev = chan;
+ continue; /* can use same entry */
+ }
+
+ if (start && prev) {
+ pos = hostapd_eid_country_add(hapd, pos, end,
+ chan_spacing,
+ start, prev);
+ start = NULL;
+ }
+
+ /* Start new group */
+ start = prev = chan;
+ }
+
+ if (start) {
+ pos = hostapd_eid_country_add(hapd, pos, end, chan_spacing,
+ start, prev);
+ }
+
+ return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+ int max_len)
+{
+ u8 *pos = eid;
+ u8 *end = eid + max_len;
+
+ if (!hapd->iconf->ieee80211d || max_len < 6 ||
+ hapd->iface->current_mode == NULL)
+ return eid;
+
+ *pos++ = WLAN_EID_COUNTRY;
+ pos++; /* length will be set later */
+ os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+ pos += 3;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ /* Force the third octet of the country string to indicate
+ * Global Operating Class (Table E-4) */
+ eid[4] = 0x04;
+
+ /* Operating Triplet field */
+ /* Operating Extension Identifier (>= 201 to indicate this is
+ * not a Subband Triplet field) */
+ *pos++ = 201;
+ /* Operating Class */
+ *pos++ = hapd->iconf->op_class;
+ /* Coverage Class */
+ *pos++ = 0;
+ /* Subband Triplets are required only for the 20 MHz case */
+ if (hapd->iconf->op_class == 131 ||
+ hapd->iconf->op_class == 136)
+ pos = hostapd_fill_subband_triplets(hapd, pos, end);
+ } else {
+ pos = hostapd_fill_subband_triplets(hapd, pos, end);
+ }
+
+ if ((pos - eid) & 1) {
+ if (end - pos < 1)
+ return eid;
+ *pos++ = 0; /* pad for 16-bit alignment */
+ }
+
+ eid[1] = (pos - eid) - 2;
+
+ return pos;
+}
+
+
+const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid)
+{
+ const u8 *ies;
+ size_t ies_len;
+
+ ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
+ if (!ies)
+ return NULL;
+
+ return get_ie(ies, ies_len, eid);
+}
+
+
+static const u8 * hostapd_vendor_wpa_ie(struct hostapd_data *hapd,
+ u32 vendor_type)
+{
+ const u8 *ies;
+ size_t ies_len;
+
+ ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
+ if (!ies)
+ return NULL;
+
+ return get_vendor_ie(ies, ies_len, vendor_type);
+}
+
+
+static u8 * hostapd_get_rsne(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_mde(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsnxe(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+ const u8 *ie;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->no_beacon_rsnxe) {
+ wpa_printf(MSG_INFO, "TESTING: Do not add RSNXE into Beacon");
+ return pos;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, WPA_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
+ return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!hapd->cs_freq_params.channel)
+ return eid;
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH;
+ *eid++ = 3;
+ *eid++ = hapd->cs_block_tx;
+ *eid++ = hapd->cs_freq_params.channel;
+ *eid++ = hapd->cs_count;
+
+ return eid;
+}
+
+
+static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
+{
+ if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
+ return eid;
+
+ *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
+ *eid++ = 4;
+ *eid++ = hapd->cs_block_tx;
+ *eid++ = hapd->iface->cs_oper_class;
+ *eid++ = hapd->cs_freq_params.channel;
+ *eid++ = hapd->cs_count;
+
+ return eid;
+}
+
+
+static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 op_class, channel;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
+ !hapd->iface->freq)
+ return eid;
+
+ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return eid;
+
+ *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
+ *eid++ = 2;
+
+ /* Current Operating Class */
+ *eid++ = op_class;
+
+ /* TODO: Advertise all the supported operating classes */
+ *eid++ = 0;
+
+ return eid;
+}
+
+
+static int
+ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *tx_bss;
+ size_t len, rnr_len = 0;
+ u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
+ u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
+
+ if (!iface->mbssid_max_interfaces ||
+ iface->num_bss > iface->mbssid_max_interfaces ||
+ (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ !iface->ema_max_periodicity))
+ goto fail;
+
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
+ NULL, 0, &rnr_len);
+ if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ elem_count > iface->ema_max_periodicity))
+ goto fail;
+
+ elem = os_zalloc(len);
+ if (!elem)
+ goto fail;
+
+ elem_offset = os_zalloc(elem_count * sizeof(u8 *));
+ if (!elem_offset)
+ goto fail;
+
+ if (rnr_len) {
+ rnr_elem = os_zalloc(rnr_len);
+ if (!rnr_elem)
+ goto fail;
+
+ rnr_elem_offset = os_calloc(elem_count + 1, sizeof(u8 *));
+ if (!rnr_elem_offset)
+ goto fail;
+ }
+
+ end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
+ elem_count, elem_offset, NULL, 0, rnr_elem,
+ &rnr_elem_count, rnr_elem_offset, rnr_len);
+
+ params->mbssid_tx_iface = tx_bss->conf->iface;
+ params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+ params->mbssid_elem = elem;
+ params->mbssid_elem_len = end - elem;
+ params->mbssid_elem_count = elem_count;
+ params->mbssid_elem_offset = elem_offset;
+ params->rnr_elem = rnr_elem;
+ params->rnr_elem_len = rnr_len;
+ params->rnr_elem_count = rnr_elem_count;
+ params->rnr_elem_offset = rnr_elem_offset;
+ if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
+ params->ema = true;
+
+ return 0;
+
+fail:
+ os_free(rnr_elem);
+ os_free(rnr_elem_offset);
+ os_free(elem_offset);
+ os_free(elem);
+ wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
+ return -1;
+}
+
+
+static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
+ u8 mbssid_elem_count)
+{
+ struct hostapd_iface *iface = hapd->iface;
+
+ if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 3;
+ *eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+ *eid++ = iface->num_bss;
+ *eid++ = mbssid_elem_count;
+ }
+
+ return eid;
+}
+
+
+static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *req,
+ int is_p2p, size_t *resp_len,
+ const u8 *known_bss, u8 known_bss_len)
+{
+ struct ieee80211_mgmt *resp;
+ u8 *pos, *epos, *csa_pos;
+ size_t buflen;
+
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
+#define MAX_PROBERESP_LEN 768
+ buflen = MAX_PROBERESP_LEN;
+#ifdef CONFIG_WPS
+ if (hapd->wps_probe_resp_ie)
+ buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_probe_resp_ie)
+ buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies)
+ buflen += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
+ if (hapd->conf->vendor_elements)
+ buflen += wpabuf_len(hapd->conf->vendor_elements);
+ if (hapd->conf->vendor_vht) {
+ buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+ 3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+ /* An additional Transmit Power Envelope element for
+ * subordinate client */
+ if (hapd->iconf->he_6ghz_reg_pwr_type ==
+ HE_6GHZ_INDOOR_AP)
+ buflen += 4;
+ }
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+ buflen += 3 + sizeof(struct ieee80211_eht_operation);
+ if (hapd->iconf->punct_bitmap)
+ buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+
+ /*
+ * TODO: Multi-Link element has variable length and can be
+ * long based on the common info and number of per
+ * station profiles. For now use 256.
+ */
+ if (hapd->conf->mld_ap)
+ buflen += 256;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+ known_bss, known_bss_len, NULL);
+ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ buflen += hostapd_mbo_ie_len(hapd);
+ buflen += hostapd_eid_owe_trans_len(hapd);
+ buflen += hostapd_eid_dpp_cc_len(hapd);
+
+ resp = os_zalloc(buflen);
+ if (resp == NULL)
+ return NULL;
+
+ epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
+
+ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_PROBE_RESP);
+ /* Unicast the response to all requests on bands other than 6 GHz. For
+ * the 6 GHz, unicast is used only if the actual SSID is not included in
+ * the Beacon frames. Otherwise, broadcast response is used per IEEE
+ * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
+ * the Probe Response frame template for the unsolicited (i.e., not as
+ * a response to a specific request) case. */
+ if (req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
+ hapd->conf->ignore_broadcast_ssid))
+ os_memcpy(resp->da, req->sa, ETH_ALEN);
+ else
+ os_memset(resp->da, 0xff, ETH_ALEN);
+
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ resp->u.probe_resp.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ resp->u.probe_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+
+ pos = resp->u.probe_resp.variable;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+
+ /* Supported rates */
+ pos = hostapd_eid_supp_rates(hapd, pos);
+
+ /* DS Params */
+ pos = hostapd_eid_ds_params(hapd, pos);
+
+ pos = hostapd_eid_country(hapd, pos, epos - pos);
+
+ /* Power Constraint element */
+ pos = hostapd_eid_pwr_constraint(hapd, pos);
+
+ /* CSA IE */
+ csa_pos = hostapd_eid_csa(hapd, pos);
+ if (csa_pos != pos)
+ hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
+ pos = csa_pos;
+
+ /* ERP Information element */
+ pos = hostapd_eid_erp_info(hapd, pos);
+
+ /* Extended supported rates */
+ pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+ pos = hostapd_get_rsne(hapd, pos, epos - pos);
+ pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+ pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+ NULL, known_bss, known_bss_len, NULL, NULL,
+ NULL, 0);
+ pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+ pos = hostapd_get_mde(hapd, pos, epos - pos);
+
+ /* eCSA IE */
+ csa_pos = hostapd_eid_ecsa(hapd, pos);
+ if (csa_pos != pos)
+ hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
+ pos = csa_pos;
+
+ pos = hostapd_eid_supported_op_classes(hapd, pos);
+ pos = hostapd_eid_ht_capabilities(hapd, pos);
+ pos = hostapd_eid_ht_operation(hapd, pos);
+
+ /* Probe Response frames always include all non-TX profiles except
+ * when a list of known BSSes is included in the Probe Request frame. */
+ pos = hostapd_eid_ext_capab(hapd, pos,
+ hapd->iconf->mbssid >= MBSSID_ENABLED &&
+ !known_bss_len);
+
+ pos = hostapd_eid_time_adv(hapd, pos);
+ pos = hostapd_eid_time_zone(hapd, pos);
+
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
+ wpabuf_len(hapd->iface->fst_ies));
+ pos += wpabuf_len(hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+ !is_6ghz_op_class(hapd->iconf->op_class)) {
+ pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+ pos = hostapd_eid_txpower_envelope(hapd, pos);
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
+ is_6ghz_op_class(hapd->iconf->op_class))
+ pos = hostapd_eid_txpower_envelope(hapd, pos);
+#endif /* CONFIG_IEEE80211AX */
+
+ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+ pos = hostapd_eid_fils_indic(hapd, pos, 0);
+ pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ u8 *cca_pos;
+
+ pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
+ pos = hostapd_eid_he_operation(hapd, pos);
+
+ /* BSS Color Change Announcement element */
+ cca_pos = hostapd_eid_cca(hapd, pos);
+ if (cca_pos != pos)
+ hapd->cca_c_off_proberesp = cca_pos - (u8 *) resp - 2;
+ pos = cca_pos;
+
+ pos = hostapd_eid_spatial_reuse(hapd, pos);
+ pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+ pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hapd->conf->mld_ap)
+ pos = hostapd_eid_eht_basic_ml(hapd, pos, NULL, true);
+ pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
+ pos = hostapd_eid_eht_operation(hapd, pos);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht)
+ pos = hostapd_eid_vendor_vht(hapd, pos);
+#endif /* CONFIG_IEEE80211AC */
+
+ /* WPA / OSEN */
+ pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
+ pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
+
+ /* Wi-Fi Alliance WMM */
+ pos = hostapd_eid_wmm(hapd, pos);
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+ os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
+ wpabuf_len(hapd->wps_probe_resp_ie));
+ pos += wpabuf_len(hapd->wps_probe_resp_ie);
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+ hapd->p2p_probe_resp_ie) {
+ os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+ wpabuf_len(hapd->p2p_probe_resp_ie));
+ pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+ if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ P2P_MANAGE)
+ pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+ pos = hostapd_eid_hs20_indication(hapd, pos);
+#endif /* CONFIG_HS20 */
+
+ pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
+ pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
+ pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
+
+ if (hapd->conf->vendor_elements) {
+ os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
+ wpabuf_len(hapd->conf->vendor_elements));
+ pos += wpabuf_len(hapd->conf->vendor_elements);
+ }
+
+ *resp_len = pos - (u8 *) resp;
+ return (u8 *) resp;
+}
+
+
+enum ssid_match_result {
+ NO_SSID_MATCH,
+ EXACT_SSID_MATCH,
+ WILDCARD_SSID_MATCH,
+ CO_LOCATED_SSID_MATCH,
+};
+
+static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *ssid_list,
+ size_t ssid_list_len,
+ const u8 *short_ssid_list,
+ size_t short_ssid_list_len)
+{
+ const u8 *pos, *end;
+ struct hostapd_iface *iface = hapd->iface;
+ int wildcard = 0;
+ size_t i, j;
+
+ if (ssid_len == 0)
+ wildcard = 1;
+ if (ssid_len == hapd->conf->ssid.ssid_len &&
+ os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
+ return EXACT_SSID_MATCH;
+
+ if (ssid_list) {
+ pos = ssid_list;
+ end = ssid_list + ssid_list_len;
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos)
+ break;
+ if (pos[1] == 0)
+ wildcard = 1;
+ if (pos[1] == hapd->conf->ssid.ssid_len &&
+ os_memcmp(pos + 2, hapd->conf->ssid.ssid,
+ pos[1]) == 0)
+ return EXACT_SSID_MATCH;
+ pos += 2 + pos[1];
+ }
+ }
+
+ if (short_ssid_list) {
+ pos = short_ssid_list;
+ end = short_ssid_list + short_ssid_list_len;
+ while (end - pos >= 4) {
+ if (hapd->conf->ssid.short_ssid == WPA_GET_LE32(pos))
+ return EXACT_SSID_MATCH;
+ pos += 4;
+ }
+ }
+
+ if (wildcard)
+ return WILDCARD_SSID_MATCH;
+
+ if (!iface->interfaces || iface->interfaces->count <= 1 ||
+ is_6ghz_op_class(hapd->iconf->op_class))
+ return NO_SSID_MATCH;
+
+ for (i = 0; i < iface->interfaces->count; i++) {
+ struct hostapd_iface *colocated;
+
+ colocated = iface->interfaces->iface[i];
+
+ if (colocated == iface ||
+ !is_6ghz_op_class(colocated->conf->op_class))
+ continue;
+
+ for (j = 0; j < colocated->num_bss; j++) {
+ struct hostapd_bss_config *conf;
+
+ conf = colocated->bss[j]->conf;
+ if (ssid_len == conf->ssid.ssid_len &&
+ os_memcmp(ssid, conf->ssid.ssid, ssid_len) == 0)
+ return CO_LOCATED_SSID_MATCH;
+ }
+ }
+
+ return NO_SSID_MATCH;
+}
+
+
+void sta_track_expire(struct hostapd_iface *iface, int force)
+{
+ struct os_reltime now;
+ struct hostapd_sta_info *info;
+
+ if (!iface->num_sta_seen)
+ return;
+
+ os_get_reltime(&now);
+ while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+ list))) {
+ if (!force &&
+ !os_reltime_expired(&now, &info->last_seen,
+ iface->conf->track_sta_max_age))
+ break;
+ force = 0;
+
+ wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
+ MACSTR, iface->bss[0]->conf->iface,
+ MAC2STR(info->addr));
+ dl_list_del(&info->list);
+ iface->num_sta_seen--;
+ sta_track_del(info);
+ }
+}
+
+
+static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
+ const u8 *addr)
+{
+ struct hostapd_sta_info *info;
+
+ dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
+ if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+ return info;
+
+ return NULL;
+}
+
+
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
+{
+ struct hostapd_sta_info *info;
+
+ info = sta_track_get(iface, addr);
+ if (info) {
+ /* Move the most recent entry to the end of the list */
+ dl_list_del(&info->list);
+ dl_list_add_tail(&iface->sta_seen, &info->list);
+ os_get_reltime(&info->last_seen);
+ info->ssi_signal = ssi_signal;
+ return;
+ }
+
+ /* Add a new entry */
+ info = os_zalloc(sizeof(*info));
+ if (info == NULL)
+ return;
+ os_memcpy(info->addr, addr, ETH_ALEN);
+ os_get_reltime(&info->last_seen);
+ info->ssi_signal = ssi_signal;
+
+ if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+ /* Expire oldest entry to make room for a new one */
+ sta_track_expire(iface, 1);
+ }
+
+ wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
+ MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
+ dl_list_add_tail(&iface->sta_seen, &info->list);
+ iface->num_sta_seen++;
+}
+
+
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+ const char *ifname)
+{
+ struct hapd_interfaces *interfaces = iface->interfaces;
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_data *hapd = NULL;
+
+ iface = interfaces->iface[i];
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ break;
+ hapd = NULL;
+ }
+
+ if (hapd && sta_track_get(iface, addr))
+ return hapd;
+ }
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_TAXONOMY
+void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
+ struct wpabuf **probe_ie_taxonomy)
+{
+ struct hostapd_sta_info *info;
+
+ info = sta_track_get(iface, addr);
+ if (!info)
+ return;
+
+ wpabuf_free(*probe_ie_taxonomy);
+ *probe_ie_taxonomy = info->probe_ie_taxonomy;
+ info->probe_ie_taxonomy = NULL;
+}
+#endif /* CONFIG_TAXONOMY */
+
+
+void handle_probe_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int ssi_signal)
+{
+ u8 *resp;
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ie_len;
+ size_t i, resp_len;
+ int noack;
+ enum ssid_match_result res;
+ int ret;
+ u16 csa_offs[2];
+ size_t csa_offs_len;
+ struct radius_sta rad_info;
+ struct hostapd_ubus_request req = {
+ .type = HOSTAPD_UBUS_PROBE_REQ,
+ .mgmt_frame = mgmt,
+ .ssi_signal = ssi_signal,
+ .elems = &elems,
+ };
+
+ if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
+ ssi_signal < hapd->iconf->rssi_ignore_probe_request)
+ return;
+
+ if (len < IEEE80211_HDRLEN)
+ return;
+ ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
+ if (hapd->iconf->track_sta_max_num)
+ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
+ ie_len = len - IEEE80211_HDRLEN;
+
+ ret = hostapd_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+ &rad_info, 1);
+ if (ret == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Probe Request frame from " MACSTR
+ " due to ACL reject ", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+ if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+ mgmt->sa, mgmt->da, mgmt->bssid,
+ ie, ie_len, ssi_signal) > 0)
+ return;
+
+ if (!hapd->conf->send_probe_response)
+ return;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if ((!elems.ssid || !elems.supp_rates)) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+ "without SSID or supported rates element",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ /*
+ * No need to reply if the Probe Request frame was sent on an adjacent
+ * channel. IEEE Std 802.11-2012 describes this as a requirement for an
+ * AP with dot11RadioMeasurementActivated set to true, but strictly
+ * speaking does not allow such ignoring of Probe Request frames if
+ * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
+ * number of unnecessary Probe Response frames for cases where the STA
+ * is less likely to see them (Probe Request frame sent on a
+ * neighboring, but partially overlapping, channel).
+ */
+ if (elems.ds_params &&
+ hapd->iface->current_mode &&
+ (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+ hapd->iconf->channel != elems.ds_params[0]) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
+ hapd->iconf->channel, elems.ds_params[0]);
+ return;
+ }
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p && hapd->p2p_group && elems.wps_ie) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Requested Device "
+ "Type");
+ wpabuf_free(wps);
+ return;
+ }
+ wpabuf_free(wps);
+ }
+
+ if (hapd->p2p && hapd->p2p_group && elems.p2p) {
+ struct wpabuf *p2p;
+ p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
+ if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Device ID");
+ wpabuf_free(p2p);
+ return;
+ }
+ wpabuf_free(p2p);
+ }
+#endif /* CONFIG_P2P */
+
+ if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
+ elems.ssid_list_len == 0 && elems.short_ssid_list_len == 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+ "broadcast SSID ignored", MAC2STR(mgmt->sa));
+ return;
+ }
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0) {
+ /* Process P2P Wildcard SSID like Wildcard SSID */
+ elems.ssid_len = 0;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TAXONOMY
+ {
+ struct sta_info *sta;
+ struct hostapd_sta_info *info;
+
+ if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) {
+ taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len);
+ } else if ((info = sta_track_get(hapd->iface,
+ mgmt->sa)) != NULL) {
+ taxonomy_hostapd_sta_info_probe_req(hapd, info,
+ ie, ie_len);
+ }
+ }
+#endif /* CONFIG_TAXONOMY */
+
+ res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+ elems.ssid_list, elems.ssid_list_len,
+ elems.short_ssid_list, elems.short_ssid_list_len);
+ if (res == NO_SSID_MATCH) {
+ if (!(mgmt->da[0] & 0x01)) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for foreign SSID '%s' (DA " MACSTR ")%s",
+ MAC2STR(mgmt->sa),
+ wpa_ssid_txt(elems.ssid, elems.ssid_len),
+ MAC2STR(mgmt->da),
+ elems.ssid_list ? " (SSID list)" : "");
+ }
+ return;
+ }
+
+ if (hapd->conf->ignore_broadcast_ssid && res == WILDCARD_SSID_MATCH) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+ "broadcast SSID ignored", MAC2STR(mgmt->sa));
+ return;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ if (hapd->conf->interworking &&
+ elems.interworking && elems.interworking_len >= 1) {
+ u8 ant = elems.interworking[0] & 0x0f;
+ if (ant != INTERWORKING_ANT_WILDCARD &&
+ ant != hapd->conf->access_network_type) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching ANT %u ignored",
+ MAC2STR(mgmt->sa), ant);
+ return;
+ }
+ }
+
+ if (hapd->conf->interworking && elems.interworking &&
+ (elems.interworking_len == 7 || elems.interworking_len == 9)) {
+ const u8 *hessid;
+ if (elems.interworking_len == 7)
+ hessid = elems.interworking + 1;
+ else
+ hessid = elems.interworking + 1 + 2;
+ if (!is_broadcast_ether_addr(hessid) &&
+ os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching HESSID " MACSTR
+ " ignored",
+ MAC2STR(mgmt->sa), MAC2STR(hessid));
+ return;
+ }
+ }
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ supp_rates_11b_only(&elems)) {
+ /* Indicates support for 11b rates only */
+ wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
+ MACSTR " with only 802.11b rates",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_P2P */
+
+ if (hostapd_ubus_handle_event(hapd, &req)) {
+ wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ /* TODO: verify that supp_rates contains at least one matching rate
+ * with AP configuration */
+
+ if (hapd->conf->no_probe_resp_if_seen_on &&
+ is_multicast_ether_addr(mgmt->da) &&
+ is_multicast_ether_addr(mgmt->bssid) &&
+ sta_track_seen_on(hapd->iface, mgmt->sa,
+ hapd->conf->no_probe_resp_if_seen_on)) {
+ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ " since STA has been seen on %s",
+ hapd->conf->iface, MAC2STR(mgmt->sa),
+ hapd->conf->no_probe_resp_if_seen_on);
+ return;
+ }
+
+ if (hapd->conf->no_probe_resp_if_max_sta &&
+ is_multicast_ether_addr(mgmt->da) &&
+ is_multicast_ether_addr(mgmt->bssid) &&
+ hostapd_check_max_sta(hapd) &&
+ !ap_get_sta(hapd, mgmt->sa)) {
+ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ " since no room for additional STA",
+ hapd->conf->iface, MAC2STR(mgmt->sa));
+ return;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iconf->ignore_probe_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_probe_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring probe request from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Do not send Probe Response frame from a non-transmitting multiple
+ * BSSID profile unless the Probe Request frame is directed at that
+ * particular BSS. */
+ if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
+ return;
+
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
+ " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+
+ resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
+ &resp_len, elems.mbssid_known_bss,
+ elems.mbssid_known_bss_len);
+ if (resp == NULL)
+ return;
+
+ /*
+ * If this is a broadcast probe request, apply no ack policy to avoid
+ * excessive retries.
+ */
+ noack = !!(res == WILDCARD_SSID_MATCH &&
+ is_broadcast_ether_addr(mgmt->da));
+
+ csa_offs_len = 0;
+ if (hapd->csa_in_progress) {
+ if (hapd->cs_c_off_proberesp)
+ csa_offs[csa_offs_len++] =
+ hapd->cs_c_off_proberesp;
+
+ if (hapd->cs_c_off_ecsa_proberesp)
+ csa_offs[csa_offs_len++] =
+ hapd->cs_c_off_ecsa_proberesp;
+ }
+
+ ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
+ resp_len, noack,
+ csa_offs_len ? csa_offs : NULL,
+ csa_offs_len, 0);
+
+ if (ret < 0)
+ wpa_printf(MSG_INFO, "handle_probe_req: send failed");
+
+ os_free(resp);
+
+ wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
+ "SSID", MAC2STR(mgmt->sa),
+ elems.ssid_len == 0 ? "broadcast" : "our");
+}
+
+
+static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
+ size_t *resp_len)
+{
+ /* check probe response offloading caps and print warnings */
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
+ return NULL;
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
+ (!(hapd->iface->probe_resp_offloads &
+ (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
+ WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
+ wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
+ "Probe Response while not supporting this");
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
+ !(hapd->iface->probe_resp_offloads &
+ WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
+ wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
+ "Probe Response while not supporting this");
+#endif /* CONFIG_P2P */
+
+ if (hapd->conf->interworking &&
+ !(hapd->iface->probe_resp_offloads &
+ WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
+ wpa_printf(MSG_WARNING, "Device is trying to offload "
+ "Interworking Probe Response while not supporting "
+ "this");
+
+ /* Generate a Probe Response template for the non-P2P case */
+ return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+#ifdef CONFIG_IEEE80211AX
+/* Unsolicited broadcast Probe Response transmission, 6 GHz only */
+static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ if (!is_6ghz_op_class(hapd->iconf->op_class))
+ return NULL;
+
+ params->unsol_bcast_probe_resp_interval =
+ hapd->conf->unsol_bcast_probe_resp_interval;
+
+ return hostapd_gen_probe_resp(hapd, NULL, 0,
+ ¶ms->unsol_bcast_probe_resp_tmpl_len,
+ NULL, 0);
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
+void sta_track_del(struct hostapd_sta_info *info)
+{
+#ifdef CONFIG_TAXONOMY
+ wpabuf_free(info->probe_ie_taxonomy);
+ info->probe_ie_taxonomy = NULL;
+#endif /* CONFIG_TAXONOMY */
+ os_free(info);
+}
+
+
+#ifdef CONFIG_FILS
+
+static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd)
+{
+ u16 cap_info, phy_index = 0;
+ u8 chwidth = FD_CAP_BSS_CHWIDTH_20, mcs_nss_size = 4;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+
+ cap_info = FD_CAP_ESS;
+ if (hapd->conf->wpa)
+ cap_info |= FD_CAP_PRIVACY;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ phy_index = FD_CAP_PHY_INDEX_HE;
+
+ switch (hapd->iconf->op_class) {
+ case 137:
+ chwidth = FD_CAP_BSS_CHWIDTH_320;
+ break;
+ case 135:
+ mcs_nss_size += 4;
+ /* fallthrough */
+ case 134:
+ mcs_nss_size += 4;
+ chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
+ break;
+ case 133:
+ chwidth = FD_CAP_BSS_CHWIDTH_80;
+ break;
+ case 132:
+ chwidth = FD_CAP_BSS_CHWIDTH_40;
+ break;
+ }
+ } else {
+ switch (hostapd_get_oper_chwidth(hapd->iconf)) {
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ mcs_nss_size += 4;
+ /* fallthrough */
+ case CONF_OPER_CHWIDTH_160MHZ:
+ mcs_nss_size += 4;
+ chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ chwidth = FD_CAP_BSS_CHWIDTH_80;
+ break;
+ case CONF_OPER_CHWIDTH_USE_HT:
+ if (hapd->iconf->secondary_channel)
+ chwidth = FD_CAP_BSS_CHWIDTH_40;
+ else
+ chwidth = FD_CAP_BSS_CHWIDTH_20;
+ break;
+ default:
+ break;
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
+ phy_index = FD_CAP_PHY_INDEX_HE;
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AC
+ if (!phy_index &&
+ hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
+ phy_index = FD_CAP_PHY_INDEX_VHT;
+#endif /* CONFIG_IEEE80211AC */
+ if (!phy_index &&
+ hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
+ phy_index = FD_CAP_PHY_INDEX_HT;
+ }
+
+ cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
+ cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
+
+ if (mode && phy_index == FD_CAP_PHY_INDEX_HE) {
+ const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs;
+ int i;
+ u16 nss = 0, mcs[6];
+
+ os_memset(mcs, 0xffff, 6 * sizeof(u16));
+
+ if (mcs_nss_size == 4) {
+ mcs[0] = WPA_GET_LE16(&he_mcs[0]);
+ mcs[1] = WPA_GET_LE16(&he_mcs[2]);
+ }
+
+ if (mcs_nss_size == 8) {
+ mcs[2] = WPA_GET_LE16(&he_mcs[4]);
+ mcs[3] = WPA_GET_LE16(&he_mcs[6]);
+ }
+
+ if (mcs_nss_size == 12) {
+ mcs[4] = WPA_GET_LE16(&he_mcs[8]);
+ mcs[5] = WPA_GET_LE16(&he_mcs[10]);
+ }
+
+ for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
+ u16 nss_mask = 0x3 << (i * 2);
+
+ /*
+ * If NSS values supported by RX and TX are different
+ * then choose the smaller of the two as the maximum
+ * supported NSS as that is the value supported by
+ * both RX and TX.
+ */
+ if (mcs_nss_size == 4 &&
+ (((mcs[0] & nss_mask) == nss_mask) ||
+ ((mcs[1] & nss_mask) == nss_mask)))
+ continue;
+
+ if (mcs_nss_size == 8 &&
+ (((mcs[2] & nss_mask) == nss_mask) ||
+ ((mcs[3] & nss_mask) == nss_mask)))
+ continue;
+
+ if (mcs_nss_size == 12 &&
+ (((mcs[4] & nss_mask) == nss_mask) ||
+ ((mcs[5] & nss_mask) == nss_mask)))
+ continue;
+
+ nss++;
+ }
+
+ if (nss > 4)
+ cap_info |= FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
+ else if (nss)
+ cap_info |= (nss - 1) << FD_CAP_NSS_SHIFT;
+ }
+
+ return cap_info;
+}
+
+
+static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+{
+ struct ieee80211_mgmt *head;
+ const u8 *mobility_domain;
+ u8 *pos, *length_pos, buf[200];
+ u16 ctl = 0;
+ u8 fd_rsn_info[5];
+ size_t total_len, buf_len;
+
+ total_len = 24 + 2 + 12;
+
+ /* FILS Discovery Frame Control */
+ ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) |
+ FD_FRAME_CTL_SHORT_SSID_PRESENT |
+ FD_FRAME_CTL_LENGTH_PRESENT |
+ FD_FRAME_CTL_CAP_PRESENT;
+ total_len += 4 + 1 + 2;
+
+ /* Check for optional subfields and calculate length */
+ if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
+ ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
+ total_len += sizeof(fd_rsn_info);
+ }
+
+ mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
+ if (mobility_domain) {
+ ctl |= FD_FRAME_CTL_MD_PRESENT;
+ total_len += 3;
+ }
+
+ total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
+
+ pos = hostapd_eid_fils_indic(hapd, buf, 0);
+ buf_len = pos - buf;
+ total_len += buf_len;
+
+#ifdef CONFIG_IEEE80211AX
+ /* Transmit Power Envelope element(s) */
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ total_len += 4;
+ if (hapd->iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP)
+ total_len += 4;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ head = os_zalloc(total_len);
+ if (!head)
+ return NULL;
+
+ head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memset(head->da, 0xff, ETH_ALEN);
+ os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+
+ head->u.action.category = WLAN_ACTION_PUBLIC;
+ head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY;
+
+ pos = &head->u.action.u.public_action.variable[0];
+
+ /* FILS Discovery Information field */
+
+ /* FILS Discovery Frame Control */
+ WPA_PUT_LE16(pos, ctl);
+ pos += 2;
+
+ /* Hardware or low-level driver will fill in the Timestamp value */
+ pos += 8;
+
+ /* Beacon Interval */
+ WPA_PUT_LE16(pos, hapd->iconf->beacon_int);
+ pos += 2;
+
+ /* Short SSID */
+ WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid);
+ pos += sizeof(hapd->conf->ssid.short_ssid);
+
+ /* Store position of FILS discovery information element Length field */
+ length_pos = pos++;
+
+ /* FD Capability */
+ WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
+ pos += 2;
+
+ /* Operating Class - not present */
+
+ /* Primary Channel - not present */
+
+ /* AP Configuration Sequence Number - not present */
+
+ /* Access Network Options - not present */
+
+ /* FD RSN Information */
+ if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) {
+ os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info));
+ pos += sizeof(fd_rsn_info);
+ }
+
+ /* Channel Center Frequency Segment 1 - not present */
+
+ /* Mobility Domain */
+ if (ctl & FD_FRAME_CTL_MD_PRESENT) {
+ os_memcpy(pos, &mobility_domain[2], 3);
+ pos += 3;
+ }
+
+ /* Fill in the Length field value */
+ *length_pos = pos - (length_pos + 1);
+
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
+
+ /* FILS Indication element */
+ if (buf_len) {
+ os_memcpy(pos, buf, buf_len);
+ pos += buf_len;
+ }
+
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ pos = hostapd_eid_txpower_envelope(hapd, pos);
+
+ *len = pos - (u8 *) head;
+ wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
+ head, pos - (u8 *) head);
+ return (u8 *) head;
+}
+
+
+/* Configure FILS Discovery frame transmission parameters */
+static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ params->fd_max_int = hapd->conf->fils_discovery_max_int;
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+ params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+
+ params->fd_min_int = hapd->conf->fils_discovery_min_int;
+ if (params->fd_min_int > params->fd_max_int)
+ params->fd_min_int = params->fd_max_int;
+
+ if (params->fd_max_int)
+ return hostapd_gen_fils_discovery(hapd,
+ ¶ms->fd_frame_tmpl_len);
+
+ return NULL;
+}
+
+#endif /* CONFIG_FILS */
+
+
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ struct ieee80211_mgmt *head = NULL;
+ u8 *tail = NULL;
+ size_t head_len = 0, tail_len = 0;
+ u8 *resp = NULL;
+ size_t resp_len = 0;
+#ifdef NEED_AP_MLME
+ u16 capab_info;
+ u8 *pos, *tailpos, *tailend, *csa_pos;
+ bool complete = false;
+#endif /* NEED_AP_MLME */
+
+ os_memset(params, 0, sizeof(*params));
+
+#ifdef NEED_AP_MLME
+#define BEACON_HEAD_BUF_SIZE 256
+#define BEACON_TAIL_BUF_SIZE 512
+ head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+ tail_len = BEACON_TAIL_BUF_SIZE;
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_beacon_ie)
+ tail_len += wpabuf_len(hapd->wps_beacon_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_beacon_ie)
+ tail_len += wpabuf_len(hapd->p2p_beacon_ie);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies)
+ tail_len += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
+ if (hapd->conf->vendor_elements)
+ tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht) {
+ tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+ 3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+ /* An additional Transmit Power Envelope element for
+ * subordinate client */
+ if (hapd->iconf->he_6ghz_reg_pwr_type ==
+ HE_6GHZ_INDOOR_AP)
+ tail_len += 4;
+ }
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+ tail_len += 3 + sizeof(struct ieee80211_eht_operation);
+ if (hapd->iconf->punct_bitmap)
+ tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+
+ /*
+ * TODO: Multi-Link element has variable length and can be
+ * long based on the common info and number of per
+ * station profiles. For now use 256.
+ */
+ if (hapd->conf->mld_ap)
+ tail_len += 256;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ hapd == hostapd_mbssid_get_tx_bss(hapd))
+ tail_len += 5; /* Multiple BSSID Configuration element */
+ tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
+ tail_len += hostapd_mbo_ie_len(hapd);
+ tail_len += hostapd_eid_owe_trans_len(hapd);
+ tail_len += hostapd_eid_dpp_cc_len(hapd);
+
+ tailpos = tail = os_malloc(tail_len);
+ if (head == NULL || tail == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to set beacon data");
+ os_free(head);
+ os_free(tail);
+ return -1;
+ }
+ tailend = tail + tail_len;
+
+ head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_BEACON);
+ head->duration = host_to_le16(0);
+ os_memset(head->da, 0xff, ETH_ALEN);
+
+ os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ head->u.beacon.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ capab_info = hostapd_own_capab_info(hapd);
+ head->u.beacon.capab_info = host_to_le16(capab_info);
+ pos = &head->u.beacon.variable[0];
+
+ /* SSID */
+ *pos++ = WLAN_EID_SSID;
+ if (hapd->conf->ignore_broadcast_ssid == 2) {
+ /* clear the data, but keep the correct length of the SSID */
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memset(pos, 0, hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+ } else if (hapd->conf->ignore_broadcast_ssid) {
+ *pos++ = 0; /* empty SSID */
+ } else {
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+ }
+
+ /* Supported rates */
+ pos = hostapd_eid_supp_rates(hapd, pos);
+
+ /* DS Params */
+ pos = hostapd_eid_ds_params(hapd, pos);
+
+ head_len = pos - (u8 *) head;
+
+ tailpos = hostapd_eid_country(hapd, tailpos, tailend - tailpos);
+
+ /* Power Constraint element */
+ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
+ /* CSA IE */
+ csa_pos = hostapd_eid_csa(hapd, tailpos);
+ if (csa_pos != tailpos)
+ hapd->cs_c_off_beacon = csa_pos - tail - 1;
+ tailpos = csa_pos;
+
+ /* ERP Information element */
+ tailpos = hostapd_eid_erp_info(hapd, tailpos);
+
+ /* Extended supported rates */
+ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
+
+ tailpos = hostapd_get_rsne(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_bss_load(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
+ tailend - tailpos);
+ tailpos = hostapd_get_mde(hapd, tailpos, tailend - tailpos);
+
+ /* eCSA IE */
+ csa_pos = hostapd_eid_ecsa(hapd, tailpos);
+ if (csa_pos != tailpos)
+ hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
+ tailpos = csa_pos;
+
+ tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
+ tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+ tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+
+ if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+ if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
+ os_free(head);
+ os_free(tail);
+ wpa_printf(MSG_ERROR,
+ "MBSSID: Failed to set beacon data");
+ return -1;
+ }
+ complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
+ (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ params->mbssid_elem_count == 1);
+ }
+
+ tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
+
+ /*
+ * TODO: Time Advertisement element should only be included in some
+ * DTIM Beacon frames.
+ */
+ tailpos = hostapd_eid_time_adv(hapd, tailpos);
+
+ tailpos = hostapd_eid_interworking(hapd, tailpos);
+ tailpos = hostapd_eid_adv_proto(hapd, tailpos);
+ tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
+ wpabuf_len(hapd->iface->fst_ies));
+ tailpos += wpabuf_len(hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+ !is_6ghz_op_class(hapd->iconf->op_class)) {
+ tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
+ tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+ tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
+ is_6ghz_op_class(hapd->iconf->op_class))
+ tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AX */
+
+ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+
+ tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
+ tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+ tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+ params->mbssid_elem_count);
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ u8 *cca_pos;
+
+ tailpos = hostapd_eid_he_capab(hapd, tailpos,
+ IEEE80211_MODE_AP);
+ tailpos = hostapd_eid_he_operation(hapd, tailpos);
+
+ /* BSS Color Change Announcement element */
+ cca_pos = hostapd_eid_cca(hapd, tailpos);
+ if (cca_pos != tailpos)
+ hapd->cca_c_off_beacon = cca_pos - tail - 2;
+ tailpos = cca_pos;
+
+ tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
+ tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+ tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hapd->conf->mld_ap)
+ tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL,
+ true);
+ tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+ IEEE80211_MODE_AP);
+ tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht)
+ tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AC */
+
+ /* WPA / OSEN */
+ tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
+ tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
+
+ /* Wi-Fi Alliance WMM */
+ tailpos = hostapd_eid_wmm(hapd, tailpos);
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+ os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
+ wpabuf_len(hapd->wps_beacon_ie));
+ tailpos += wpabuf_len(hapd->wps_beacon_ie);
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
+ os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
+ wpabuf_len(hapd->p2p_beacon_ie));
+ tailpos += wpabuf_len(hapd->p2p_beacon_ie);
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+ if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ P2P_MANAGE)
+ tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+ tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+#endif /* CONFIG_HS20 */
+
+ tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
+ tailpos = hostapd_eid_owe_trans(hapd, tailpos,
+ tail + tail_len - tailpos);
+ tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
+
+ if (hapd->conf->vendor_elements) {
+ os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
+ wpabuf_len(hapd->conf->vendor_elements));
+ tailpos += wpabuf_len(hapd->conf->vendor_elements);
+ }
+
+ tail_len = tailpos > tail ? tailpos - tail : 0;
+
+ resp = hostapd_probe_resp_offloads(hapd, &resp_len);
+#endif /* NEED_AP_MLME */
+
+ params->head = (u8 *) head;
+ params->head_len = head_len;
+ params->tail = tail;
+ params->tail_len = tail_len;
+ params->proberesp = resp;
+ params->proberesp_len = resp_len;
+ params->dtim_period = hapd->conf->dtim_period;
+ params->beacon_int = hapd->iconf->beacon_int;
+ params->basic_rates = hapd->iface->basic_rates;
+ params->beacon_rate = hapd->iconf->beacon_rate;
+ params->rate_type = hapd->iconf->rate_type;
+ params->ssid = hapd->conf->ssid.ssid;
+ params->ssid_len = hapd->conf->ssid.ssid_len;
+ if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+ (WPA_PROTO_WPA | WPA_PROTO_RSN))
+ params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+ hapd->conf->rsn_pairwise;
+ else if (hapd->conf->wpa & WPA_PROTO_RSN)
+ params->pairwise_ciphers = hapd->conf->rsn_pairwise;
+ else if (hapd->conf->wpa & WPA_PROTO_WPA)
+ params->pairwise_ciphers = hapd->conf->wpa_pairwise;
+ params->group_cipher = hapd->conf->wpa_group;
+ params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+ params->auth_algs = hapd->conf->auth_algs;
+ params->wpa_version = hapd->conf->wpa;
+ params->privacy = hapd->conf->wpa;
+#ifdef CONFIG_WEP
+ params->privacy |= hapd->conf->ssid.wep.keys_set ||
+ (hapd->conf->ieee802_1x &&
+ (hapd->conf->default_wep_key_len ||
+ hapd->conf->individual_wep_key_len));
+#endif /* CONFIG_WEP */
+ switch (hapd->conf->ignore_broadcast_ssid) {
+ case 0:
+ params->hide_ssid = NO_SSID_HIDING;
+ break;
+ case 1:
+ params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
+ break;
+ case 2:
+ params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+ break;
+ }
+ params->isolate = hapd->conf->isolate;
+#ifdef NEED_AP_MLME
+ params->cts_protect = !!(ieee802_11_erp_info(hapd) &
+ ERP_INFO_USE_PROTECTION);
+ params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+ hapd->iconf->preamble == SHORT_PREAMBLE;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ params->short_slot_time =
+ hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
+ else
+ params->short_slot_time = -1;
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+ params->ht_opmode = -1;
+ else
+ params->ht_opmode = hapd->iface->ht_op_mode;
+#endif /* NEED_AP_MLME */
+ params->interworking = hapd->conf->interworking;
+ if (hapd->conf->interworking &&
+ !is_zero_ether_addr(hapd->conf->hessid))
+ params->hessid = hapd->conf->hessid;
+ params->access_network_type = hapd->conf->access_network_type;
+ params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_P2P
+ params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+ params->disable_dgaf = hapd->conf->disable_dgaf;
+ if (hapd->conf->osen) {
+ params->privacy = 1;
+ params->osen = 1;
+ }
+#endif /* CONFIG_HS20 */
+ params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
+ params->pbss = hapd->conf->pbss;
+
+ if (hapd->conf->ftm_responder) {
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
+ params->ftm_responder = 1;
+ params->lci = hapd->iface->conf->lci;
+ params->civic = hapd->iface->conf->civic;
+ } else {
+ wpa_printf(MSG_WARNING,
+ "Not configuring FTM responder as the driver doesn't advertise support for it");
+ }
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
+ !hapd->conf->disable_11be) {
+ params->mld_ap = true;
+ params->mld_link_id = hapd->mld_link_id;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
+{
+ os_free(params->tail);
+ params->tail = NULL;
+ os_free(params->head);
+ params->head = NULL;
+ os_free(params->proberesp);
+ params->proberesp = NULL;
+ os_free(params->mbssid_elem);
+ params->mbssid_elem = NULL;
+ os_free(params->mbssid_elem_offset);
+ params->mbssid_elem_offset = NULL;
+ os_free(params->rnr_elem);
+ params->rnr_elem = NULL;
+ os_free(params->rnr_elem_offset);
+ params->rnr_elem_offset = NULL;
+#ifdef CONFIG_FILS
+ os_free(params->fd_frame_tmpl);
+ params->fd_frame_tmpl = NULL;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211AX
+ os_free(params->unsol_bcast_probe_resp_tmpl);
+ params->unsol_bcast_probe_resp_tmpl = NULL;
+#endif /* CONFIG_IEEE80211AX */
+ os_free(params->allowed_freqs);
+ params->allowed_freqs = NULL;
+}
+
+
+static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+ struct wpa_driver_ap_params params;
+ struct hostapd_freq_params freq;
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_config *iconf = iface->conf;
+ struct hostapd_hw_modes *cmode = iface->current_mode;
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int res, ret = -1, i;
+ struct hostapd_hw_modes *mode;
+
+ if (!hapd->drv_priv) {
+ wpa_printf(MSG_ERROR, "Interface is disabled");
+ return -1;
+ }
+
+ hapd->beacon_set_done = 1;
+
+ if (ieee802_11_build_ap_params(hapd, ¶ms) < 0)
+ return -1;
+
+ if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+ 0)
+ goto fail;
+
+ params.beacon_ies = beacon;
+ params.proberesp_ies = proberesp;
+ params.assocresp_ies = assocresp;
+ params.reenable = hapd->reenable_beacon;
+#ifdef CONFIG_IEEE80211AX
+ params.he_spr_ctrl = hapd->iface->conf->spr.sr_control;
+ params.he_spr_non_srg_obss_pd_max_offset =
+ hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
+ params.he_spr_srg_obss_pd_min_offset =
+ hapd->iface->conf->spr.srg_obss_pd_min_offset;
+ params.he_spr_srg_obss_pd_max_offset =
+ hapd->iface->conf->spr.srg_obss_pd_max_offset;
+ os_memcpy(params.he_spr_bss_color_bitmap,
+ hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
+ os_memcpy(params.he_spr_partial_bssid_bitmap,
+ hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
+ params.he_bss_color_disabled =
+ hapd->iface->conf->he_op.he_bss_color_disabled;
+ params.he_bss_color_partial =
+ hapd->iface->conf->he_op.he_bss_color_partial;
+ params.he_bss_color = hapd->iface->conf->he_op.he_bss_color;
+ params.twt_responder = hostapd_get_he_twt_responder(hapd,
+ IEEE80211_MODE_AP);
+ params.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, ¶ms);
+#endif /* CONFIG_IEEE80211AX */
+ hapd->reenable_beacon = 0;
+#ifdef CONFIG_SAE
+ params.sae_pwe = hapd->conf->sae_pwe;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ params.fd_frame_tmpl = hostapd_fils_discovery(hapd, ¶ms);
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211BE
+ params.punct_bitmap = iconf->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (cmode &&
+ hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+ iconf->channel, iconf->enable_edmg,
+ iconf->edmg_channel, iconf->ieee80211n,
+ iconf->ieee80211ac, iconf->ieee80211ax,
+ iconf->ieee80211be,
+ iconf->secondary_channel,
+ hostapd_get_oper_chwidth(iconf),
+ hostapd_get_oper_centr_freq_seg0_idx(iconf),
+ hostapd_get_oper_centr_freq_seg1_idx(iconf),
+ cmode->vht_capab,
+ &cmode->he_capab[IEEE80211_MODE_AP],
+ &cmode->eht_capab[IEEE80211_MODE_AP]) == 0)
+ params.freq = &freq;
+
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+
+ if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
+ iconf->hw_mode != mode->mode)
+ continue;
+
+ hostapd_get_hw_mode_any_channels(hapd, mode,
+ !(iconf->acs_freq_list.num ||
+ iconf->acs_ch_list.num),
+ true, ¶ms.allowed_freqs);
+ }
+
+ res = hostapd_drv_set_ap(hapd, ¶ms);
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+ if (res)
+ wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+ else
+ ret = 0;
+fail:
+ ieee802_11_free_ap_params(¶ms);
+ return ret;
+}
+
+
+int ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ int ret;
+ size_t i, j;
+ bool is_6g;
+
+ ret = __ieee802_11_set_beacon(hapd);
+ if (ret != 0)
+ return ret;
+
+ if (!iface->interfaces || iface->interfaces->count <= 1)
+ return 0;
+
+ /* Update Beacon frames in case of 6 GHz colocation or AP MLD */
+ is_6g = is_6ghz_op_class(iface->conf->op_class);
+ for (j = 0; j < iface->interfaces->count; j++) {
+ struct hostapd_iface *other;
+ bool mld_ap = false;
+
+ other = iface->interfaces->iface[j];
+ if (other == iface || !other || !other->conf)
+ continue;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && other->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == other->bss[0]->conf->mld_id)
+ mld_ap = true;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
+ !mld_ap)
+ continue;
+
+ for (i = 0; i < other->num_bss; i++) {
+ if (other->bss[i] && other->bss[i]->started)
+ __ieee802_11_set_beacon(other->bss[i]);
+ }
+ }
+
+ return 0;
+}
+
+
+int ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->started &&
+ ieee802_11_set_beacon(iface->bss[i]) < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/* only update beacons if started */
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
+{
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
+ ieee802_11_set_beacon(iface->bss[i]) < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.h
new file mode 100644
index 0000000..c320825
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BEACON_H
+#define BEACON_H
+
+struct ieee80211_mgmt;
+
+void handle_probe_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int ssi_signal);
+int ieee802_11_set_beacon(struct hostapd_data *hapd);
+int ieee802_11_set_beacons(struct hostapd_iface *iface);
+int ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params);
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
+void sta_track_del(struct hostapd_sta_info *info);
+void sta_track_expire(struct hostapd_iface *iface, int force);
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+ const char *ifname);
+void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
+ struct wpabuf **probe_ie_taxonomy);
+
+const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid);
+
+#endif /* BEACON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.c
new file mode 100644
index 0000000..725d3cd
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.c
@@ -0,0 +1,99 @@
+/*
+ * BSS Load Element / Channel Utilization
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "bss_load.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static int get_bss_load_update_timeout(struct hostapd_data *hapd,
+ unsigned int *sec, unsigned int *usec)
+{
+ unsigned int update_period = hapd->conf->bss_load_update_period;
+ unsigned int beacon_int = hapd->iconf->beacon_int;
+ unsigned int update_timeout;
+
+ if (!update_period || !beacon_int) {
+ wpa_printf(MSG_ERROR,
+ "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
+ update_period, beacon_int);
+ return -1;
+ }
+
+ update_timeout = update_period * beacon_int;
+
+ *sec = ((update_timeout / 1000) * 1024) / 1000;
+ *usec = (update_timeout % 1000) * 1024;
+
+ return 0;
+}
+
+
+static void update_channel_utilization(void *eloop_data, void *user_data)
+{
+ struct hostapd_data *hapd = eloop_data;
+ unsigned int sec, usec;
+ int err;
+ struct hostapd_iface *iface = hapd->iface;
+
+ if (!(hapd->beacon_set_done && hapd->started))
+ return;
+
+ err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
+ if (err) {
+ wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
+ return;
+ }
+
+ ieee802_11_set_beacon(hapd);
+
+ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+ return;
+
+ if (hapd->conf->chan_util_avg_period) {
+ iface->chan_util_samples_sum += iface->channel_utilization;
+ iface->chan_util_num_sample_periods +=
+ hapd->conf->bss_load_update_period;
+ if (iface->chan_util_num_sample_periods >=
+ hapd->conf->chan_util_avg_period) {
+ iface->chan_util_average =
+ iface->chan_util_samples_sum /
+ (iface->chan_util_num_sample_periods /
+ hapd->conf->bss_load_update_period);
+ iface->chan_util_samples_sum = 0;
+ iface->chan_util_num_sample_periods = 0;
+ }
+ }
+
+ eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+ NULL);
+}
+
+
+int bss_load_update_init(struct hostapd_data *hapd)
+{
+ unsigned int sec, usec;
+
+ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+ return -1;
+
+ eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+ NULL);
+ return 0;
+}
+
+
+void bss_load_update_deinit(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.h
new file mode 100644
index 0000000..ac3c793
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.h
@@ -0,0 +1,17 @@
+/*
+ * BSS load update
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_LOAD_UPDATE_H
+#define BSS_LOAD_UPDATE_H
+
+
+int bss_load_update_init(struct hostapd_data *hapd);
+void bss_load_update_deinit(struct hostapd_data *hapd);
+
+
+#endif /* BSS_LOAD_UPDATE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.c
new file mode 100644
index 0000000..8d9f21b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.c
@@ -0,0 +1,139 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "comeback_token.h"
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
+ addr, ETH_ALEN, hash) < 0)
+ return -1;
+ *idx = hash[0];
+ return 0;
+}
+
+
+int check_comeback_token(const u8 *comeback_key,
+ u16 *comeback_pending_idx, const u8 *addr,
+ const u8 *token, size_t token_len)
+{
+ u8 mac[SHA256_MAC_LEN];
+ const u8 *addrs[2];
+ size_t len[2];
+ u16 token_idx;
+ u8 idx;
+
+ if (token_len != SHA256_MAC_LEN ||
+ comeback_token_hash(comeback_key, addr, &idx) < 0)
+ return -1;
+ token_idx = comeback_pending_idx[idx];
+ if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+ wpa_printf(MSG_DEBUG,
+ "Comeback: Invalid anti-clogging token from "
+ MACSTR " - token_idx 0x%04x, expected 0x%04x",
+ MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+ return -1;
+ }
+
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = token;
+ len[1] = 2;
+ if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+ 2, addrs, len, mac) < 0 ||
+ os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+ return -1;
+
+ comeback_pending_idx[idx] = 0; /* invalidate used token */
+
+ return 0;
+}
+
+
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+ u8 *comeback_key, u16 comeback_idx,
+ u16 *comeback_pending_idx, size_t idx_len,
+ int group, const u8 *addr, int h2e)
+{
+ struct wpabuf *buf;
+ u8 *token;
+ struct os_reltime now;
+ u8 idx[2];
+ const u8 *addrs[2];
+ size_t len[2];
+ u8 p_idx;
+ u16 token_idx;
+
+ os_get_reltime(&now);
+ if (!os_reltime_initialized(last_comeback_key_update) ||
+ os_reltime_expired(&now, last_comeback_key_update, 60) ||
+ comeback_idx == 0xffff) {
+ if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
+ comeback_key, COMEBACK_KEY_SIZE);
+ *last_comeback_key_update = now;
+ comeback_idx = 0;
+ os_memset(comeback_pending_idx, 0, idx_len);
+ }
+
+ buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
+ if (buf == NULL)
+ return NULL;
+
+ if (group)
+ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+ if (h2e) {
+ /* Encapsulate Anti-clogging Token field in a container IE */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
+ }
+
+ if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ token_idx = comeback_pending_idx[p_idx];
+ if (!token_idx) {
+ comeback_idx++;
+ token_idx = comeback_idx;
+ comeback_pending_idx[p_idx] = token_idx;
+ }
+ WPA_PUT_BE16(idx, token_idx);
+ token = wpabuf_put(buf, SHA256_MAC_LEN);
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = idx;
+ len[1] = sizeof(idx);
+ if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+ 2, addrs, len, token) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ WPA_PUT_BE16(token, token_idx);
+
+ return buf;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.h
new file mode 100644
index 0000000..d5de9e6
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.h
@@ -0,0 +1,21 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMEBACK_TOKEN_H
+#define COMEBACK_TOKEN_H
+
+int check_comeback_token(const u8 *comeback_key,
+ u16 *comeback_pending_idx, const u8 *addr,
+ const u8 *token, size_t token_len);
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+ u8 *comeback_key, u16 comeback_idx,
+ u16 *comeback_pending_idx, size_t idx_len,
+ int group, const u8 *addr, int h2e);
+
+#endif /* COMEBACK_TOKEN_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.c
new file mode 100644
index 0000000..50a4dc4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.c
@@ -0,0 +1,1501 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "fst/fst_ctrl_iface.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
+#include "ctrl_iface_ap.h"
+#include "ap_drv_ops.h"
+#include "mbo_ap.h"
+#include "taxonomy.h"
+#include "wnm_ap.h"
+
+static const char * hw_mode_str(enum hostapd_hw_mode mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return "b";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "g";
+ case HOSTAPD_MODE_IEEE80211A:
+ return "a";
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "ad";
+ case HOSTAPD_MODE_IEEE80211ANY:
+ return "any";
+ case NUM_HOSTAPD_MODES:
+ return "invalid";
+ }
+ return "unknown";
+}
+
+#ifdef CONFIG_CTRL_IFACE_MIB
+
+static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ size_t curr_len, const u8 *mcs_set)
+{
+ int ret;
+ size_t len = curr_len;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "ht_mcs_bitmask=");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* 77 first bits (+ 3 reserved bits) */
+ len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
+
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (os_snprintf_error(buflen - len, ret))
+ return curr_len;
+ len += ret;
+
+ return len;
+}
+
+
+static int hostapd_get_sta_conn_time(struct sta_info *sta,
+ struct hostap_sta_driver_data *data,
+ char *buf, size_t buflen)
+{
+ struct os_reltime age;
+ unsigned long secs;
+ int ret;
+
+ if (sta->connected_time.sec) {
+ /* Locally maintained time in AP mode */
+ os_reltime_age(&sta->connected_time, &age);
+ secs = (unsigned long) age.sec;
+ } else if (data->flags & STA_DRV_DATA_CONN_TIME) {
+ /* Time from the driver in mesh mode */
+ secs = data->connected_sec;
+ } else {
+ return 0;
+ }
+
+ ret = os_snprintf(buf, buflen, "connected_time=%lu\n", secs);
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ return ret;
+}
+
+
+static int hostapd_get_sta_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ struct hostap_sta_driver_data data;
+ int ret;
+ int len = 0;
+
+ if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+ return 0;
+
+ ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
+ "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
+ "signal=%d\n",
+ data.rx_packets, data.tx_packets,
+ data.rx_bytes, data.tx_bytes, data.inactive_msec,
+ data.signal);
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
+ data.current_rx_rate / 100);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ if (data.flags & STA_DRV_DATA_RX_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+ data.rx_mcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+ data.rx_vhtmcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+ data.rx_vht_nss);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
+ ret = os_snprintf(buf + len, buflen - len, " shortGI");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
+ data.current_tx_rate / 100);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ if (data.flags & STA_DRV_DATA_TX_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+ data.tx_mcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+ data.tx_vhtmcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+ data.tx_vht_nss);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
+ ret = os_snprintf(buf + len, buflen - len, " shortGI");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "rx_vht_mcs_map=%04x\n"
+ "tx_vht_mcs_map=%04x\n",
+ le_to_host16(sta->vht_capabilities->
+ vht_supported_mcs_set.rx_map),
+ le_to_host16(sta->vht_capabilities->
+ vht_supported_mcs_set.tx_map));
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ sta->ht_capabilities->
+ supported_mcs_set);
+ }
+
+ if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "last_ack_signal=%d\n", data.last_ack_rssi);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ len += hostapd_get_sta_conn_time(sta, &data, buf + len, buflen - len);
+
+ return len;
+}
+
+
+static const char * timeout_next_str(int val)
+{
+ switch (val) {
+ case STA_NULLFUNC:
+ return "NULLFUNC POLL";
+ case STA_DISASSOC:
+ return "DISASSOC";
+ case STA_DEAUTH:
+ return "DEAUTH";
+ case STA_REMOVE:
+ return "REMOVE";
+ case STA_DISASSOC_FROM_CLI:
+ return "DISASSOC_FROM_CLI";
+ default:
+ return "?";
+ }
+}
+
+
+static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len, res, ret, i;
+ const char *keyid;
+ const u8 *dpp_pkhash;
+
+ if (!sta)
+ return 0;
+
+ len = 0;
+ ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
+ MAC2STR(sta->addr));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
+ if (ret < 0)
+ return len;
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
+ "listen_interval=%d\nsupported_rates=",
+ sta->aid, sta->capability, sta->listen_interval);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ for (i = 0; i < sta->supported_rates_len; i++) {
+ ret = os_snprintf(buf + len, buflen - len, "%02x%s",
+ sta->supported_rates[i],
+ i + 1 < sta->supported_rates_len ? " " : "");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
+ timeout_next_str(sta->timeout_next));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
+ buflen - len);
+ if (res >= 0)
+ len += res;
+ res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+
+ len += hostapd_get_sta_info(hapd, sta, buf + len, buflen - len);
+
+#ifdef CONFIG_SAE
+ if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
+ res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
+ sta->sae->group);
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+
+ if (sta->sae && sta->sae->tmp) {
+ const u8 *pos;
+ unsigned int j, count;
+ struct wpabuf *groups = sta->sae->tmp->peer_rejected_groups;
+
+ res = os_snprintf(buf + len, buflen - len,
+ "sae_rejected_groups=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+
+ if (groups) {
+ pos = wpabuf_head(groups);
+ count = wpabuf_len(groups) / 2;
+ } else {
+ pos = NULL;
+ count = 0;
+ }
+ for (j = 0; pos && j < count; j++) {
+ res = os_snprintf(buf + len, buflen - len, "%s%d",
+ j == 0 ? "" : " ", WPA_GET_LE16(pos));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ pos += 2;
+ }
+
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_SAE */
+
+ if (sta->vlan_id > 0) {
+ res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
+ sta->vlan_id);
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+
+ res = mbo_ap_get_info(sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+
+ if (sta->supp_op_classes &&
+ buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
+ len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ sta->supp_op_classes + 1,
+ sta->supp_op_classes[0]);
+ len += os_snprintf(buf + len, buflen - len, "\n");
+ }
+
+ if (sta->power_capab) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "min_txpower=%d\n"
+ "max_txpower=%d\n",
+ sta->min_tx_power, sta->max_tx_power);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+#ifdef CONFIG_IEEE80211AC
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ res = os_snprintf(buf + len, buflen - len,
+ "vht_caps_info=0x%08x\n",
+ le_to_host32(sta->vht_capabilities->
+ vht_capabilities_info));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+ res = os_snprintf(buf + len, buflen - len,
+ "ht_caps_info=0x%04x\n",
+ le_to_host16(sta->ht_capabilities->
+ ht_capabilities_info));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+
+ if (sta->ext_capability &&
+ buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
+ len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ sta->ext_capability + 1,
+ sta->ext_capability[0]);
+ len += os_snprintf(buf + len, buflen - len, "\n");
+ }
+
+ if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "wds_sta_ifname=%s\n", sta->ifname_wds);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+ if (dpp_pkhash) {
+ ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ len += wpa_snprintf_hex(buf + len, buflen - len, dpp_pkhash,
+ SHA256_MAC_LEN);
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ return len;
+}
+
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
+}
+
+
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ int ret;
+ const char *pos;
+ struct sta_info *sta;
+
+ if (hwaddr_aton(txtaddr, addr)) {
+ ret = os_snprintf(buf, buflen, "FAIL\n");
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ return ret;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL)
+ return -1;
+
+ pos = os_strchr(txtaddr, ' ');
+ if (pos) {
+ pos++;
+
+#ifdef HOSTAPD_DUMP_STATE
+ if (os_strcmp(pos, "eapol") == 0) {
+ if (sta->eapol_sm == NULL)
+ return -1;
+ return eapol_auth_dump_state(sta->eapol_sm, buf,
+ buflen);
+ }
+#endif /* HOSTAPD_DUMP_STATE */
+
+ return -1;
+ }
+
+ ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+ ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
+
+ return ret;
+}
+
+
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ int ret;
+
+ if (hwaddr_aton(txtaddr, addr) ||
+ (sta = ap_get_sta(hapd, addr)) == NULL) {
+ ret = os_snprintf(buf, buflen, "FAIL\n");
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ return ret;
+ }
+
+ if (!sta->next)
+ return 0;
+
+ return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+}
+
+#endif
+
+#ifdef CONFIG_P2P_MANAGER
+static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+ u8 minor_reason_code, const u8 *addr)
+{
+ struct ieee80211_mgmt *mgmt;
+ int ret;
+ u8 *pos;
+
+ mgmt = os_zalloc(sizeof(*mgmt) + 100);
+ if (mgmt == NULL)
+ return -1;
+
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+ " with minor reason code %u (stype=%u (%s))",
+ MAC2STR(addr), minor_reason_code, stype,
+ fc2str(le_to_host16(mgmt->frame_control)));
+
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ if (stype == WLAN_FC_STYPE_DEAUTH) {
+ mgmt->u.deauth.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ pos = mgmt->u.deauth.variable;
+ } else {
+ mgmt->u.disassoc.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ pos = mgmt->u.disassoc.variable;
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4 + 3 + 1;
+ WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+ pos += 4;
+
+ *pos++ = P2P_ATTR_MINOR_REASON_CODE;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ *pos++ = minor_reason_code;
+
+ ret = hostapd_drv_send_mlme(hapd, mgmt, pos - (u8 *) mgmt, 0, NULL, 0,
+ 0);
+ os_free(mgmt);
+
+ return ret < 0 ? -1 : 0;
+}
+#endif /* CONFIG_P2P_MANAGER */
+
+
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ const char *pos;
+ u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
+ txtaddr);
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ pos = os_strstr(txtaddr, " reason=");
+ if (pos)
+ reason = atoi(pos + 8);
+
+ pos = os_strstr(txtaddr, " test=");
+ if (pos) {
+ struct ieee80211_mgmt mgmt;
+ int encrypt;
+
+ pos += 6;
+ encrypt = atoi(pos);
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DEAUTH);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.deauth.reason_code = host_to_le16(reason);
+ if (hostapd_drv_send_mlme(hapd, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth),
+ 0, NULL, 0, !encrypt) < 0)
+ return -1;
+ return 0;
+ }
+
+#ifdef CONFIG_P2P_MANAGER
+ pos = os_strstr(txtaddr, " p2p=");
+ if (pos) {
+ return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
+ atoi(pos + 5), addr);
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+ if (os_strstr(txtaddr, " tx=0"))
+ hostapd_drv_sta_remove(hapd, addr);
+ else
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ ap_sta_deauthenticate(hapd, sta, reason);
+ else if (addr[0] == 0xff)
+ hostapd_free_stas(hapd);
+
+ return 0;
+}
+
+
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ const char *pos;
+ u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
+ txtaddr);
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ pos = os_strstr(txtaddr, " reason=");
+ if (pos)
+ reason = atoi(pos + 8);
+
+ pos = os_strstr(txtaddr, " test=");
+ if (pos) {
+ struct ieee80211_mgmt mgmt;
+ int encrypt;
+
+ pos += 6;
+ encrypt = atoi(pos);
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DISASSOC);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.disassoc.reason_code = host_to_le16(reason);
+ if (hostapd_drv_send_mlme(hapd, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth),
+ 0, NULL, 0, !encrypt) < 0)
+ return -1;
+ return 0;
+ }
+
+#ifdef CONFIG_P2P_MANAGER
+ pos = os_strstr(txtaddr, " p2p=");
+ if (pos) {
+ return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
+ atoi(pos + 5), addr);
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+ if (os_strstr(txtaddr, " tx=0"))
+ hostapd_drv_sta_remove(hapd, addr);
+ else
+ hostapd_drv_sta_disassoc(hapd, addr, reason);
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ ap_sta_disassociate(hapd, sta, reason);
+ else if (addr[0] == 0xff)
+ hostapd_free_stas(hapd);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_TAXONOMY
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+ const char *txtaddr,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return -1;
+
+ return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return -1;
+
+ hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
+ sta->flags & WLAN_STA_WMM);
+ return 0;
+}
+
+
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ struct hostapd_config *iconf = hapd->iconf;
+ int len = 0, ret, j;
+ size_t i;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "state=%s\n"
+ "phy=%s\n"
+ "freq=%d\n"
+ "num_sta_non_erp=%d\n"
+ "num_sta_no_short_slot_time=%d\n"
+ "num_sta_no_short_preamble=%d\n"
+ "olbc=%d\n"
+ "num_sta_ht_no_gf=%d\n"
+ "num_sta_no_ht=%d\n"
+ "num_sta_ht_20_mhz=%d\n"
+ "num_sta_ht40_intolerant=%d\n"
+ "olbc_ht=%d\n"
+ "ht_op_mode=0x%x\n",
+ hostapd_state_text(iface->state),
+ iface->phy,
+ iface->freq,
+ iface->num_sta_non_erp,
+ iface->num_sta_no_short_slot_time,
+ iface->num_sta_no_short_preamble,
+ iface->olbc,
+ iface->num_sta_ht_no_gf,
+ iface->num_sta_no_ht,
+ iface->num_sta_ht_20mhz,
+ iface->num_sta_ht40_intolerant,
+ iface->olbc_ht,
+ iface->ht_op_mode);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ if (mode) {
+ ret = os_snprintf(buf + len, buflen - len, "hw_mode=%s\n",
+ hw_mode_str(mode->mode));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iconf->country[0] && iconf->country[1]) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "country_code=%c%c\ncountry3=0x%X\n",
+ iconf->country[0], iconf->country[1],
+ iconf->country[2]);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (!iface->cac_started || !iface->dfs_cac_ms) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "cac_time_seconds=%d\n"
+ "cac_time_left_seconds=N/A\n",
+ iface->dfs_cac_ms / 1000);
+ } else {
+ /* CAC started and CAC time set - calculate remaining time */
+ struct os_reltime now;
+ long left_time;
+
+ os_reltime_age(&iface->dfs_cac_start, &now);
+ left_time = (long) iface->dfs_cac_ms / 1000 - now.sec;
+ ret = os_snprintf(buf + len, buflen - len,
+ "cac_time_seconds=%u\n"
+ "cac_time_left_seconds=%lu\n",
+ iface->dfs_cac_ms / 1000,
+ left_time > 0 ? left_time : 0);
+ }
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "channel=%u\n"
+ "edmg_enable=%d\n"
+ "edmg_channel=%d\n"
+ "secondary_channel=%d\n"
+ "ieee80211n=%d\n"
+ "ieee80211ac=%d\n"
+ "ieee80211ax=%d\n"
+ "ieee80211be=%d\n"
+ "beacon_int=%u\n"
+ "dtim_period=%d\n",
+ iface->conf->channel,
+ iface->conf->enable_edmg,
+ iface->conf->edmg_channel,
+ iface->conf->ieee80211n && !hapd->conf->disable_11n ?
+ iface->conf->secondary_channel : 0,
+ iface->conf->ieee80211n && !hapd->conf->disable_11n,
+ iface->conf->ieee80211ac &&
+ !hapd->conf->disable_11ac,
+ iface->conf->ieee80211ax &&
+ !hapd->conf->disable_11ax,
+ iface->conf->ieee80211be &&
+ !hapd->conf->disable_11be,
+ iface->conf->beacon_int,
+ hapd->conf->dtim_period);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+#ifdef CONFIG_IEEE80211BE
+ if (iface->conf->ieee80211be && !hapd->conf->disable_11be) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "eht_oper_chwidth=%d\n"
+ "eht_oper_centr_freq_seg0_idx=%d\n",
+ iface->conf->eht_oper_chwidth,
+ iface->conf->eht_oper_centr_freq_seg0_idx);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AX
+ if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "he_oper_chwidth=%d\n"
+ "he_oper_centr_freq_seg0_idx=%d\n"
+ "he_oper_centr_freq_seg1_idx=%d\n",
+ iface->conf->he_oper_chwidth,
+ iface->conf->he_oper_centr_freq_seg0_idx,
+ iface->conf->he_oper_centr_freq_seg1_idx);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "vht_oper_chwidth=%d\n"
+ "vht_oper_centr_freq_seg0_idx=%d\n"
+ "vht_oper_centr_freq_seg1_idx=%d\n"
+ "vht_caps_info=%08x\n",
+ iface->conf->vht_oper_chwidth,
+ iface->conf->vht_oper_centr_freq_seg0_idx,
+ iface->conf->vht_oper_centr_freq_seg1_idx,
+ iface->conf->vht_capab);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
+ u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
+ u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "rx_vht_mcs_map=%04x\n"
+ "tx_vht_mcs_map=%04x\n",
+ rxmap, txmap);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "ht_caps_info=%04x\n",
+ hapd->iconf->ht_capab);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+#ifdef CONFIG_CTRL_IFACE_MIB
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ mode->mcs_set);
+ }
+#endif /* CONFIG_CTRL_IFACE_MIB */
+ if (iface->current_rates && iface->num_rates) {
+ ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ for (j = 0; j < iface->num_rates; j++) {
+ ret = os_snprintf(buf + len, buflen - len, "%s%02x",
+ j > 0 ? " " : "",
+ iface->current_rates[j].rate / 5);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ for (j = 0; mode && j < mode->num_channels; j++) {
+ if (mode->channels[j].freq == iface->freq) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "max_txpower=%u\n",
+ mode->channels[j].max_tx_power);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ break;
+ }
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+ ret = os_snprintf(buf + len, buflen - len,
+ "bss[%d]=%s\n"
+ "bssid[%d]=" MACSTR "\n"
+ "ssid[%d]=%s\n"
+ "num_sta[%d]=%d\n",
+ (int) i, bss->conf->iface,
+ (int) i, MAC2STR(bss->own_addr),
+ (int) i,
+ wpa_ssid_txt(bss->conf->ssid.ssid,
+ bss->conf->ssid.ssid_len),
+ (int) i, bss->num_sta);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+#ifdef CONFIG_IEEE80211BE
+ if (bss->conf->mld_ap) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "mld_addr[%d]=" MACSTR "\n"
+ "mld_id[%d]=%d\n"
+ "mld_link_id[%d]=%d\n",
+ (int) i, MAC2STR(bss->mld_addr),
+ (int) i, bss->conf->mld_id,
+ (int) i, bss->mld_link_id);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ if (hapd->conf->chan_util_avg_period) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "chan_util_avg=%u\n",
+ iface->chan_util_average);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+
+
+int hostapd_parse_csa_settings(const char *pos,
+ struct csa_settings *settings)
+{
+ char *end;
+
+ os_memset(settings, 0, sizeof(*settings));
+ settings->cs_count = strtol(pos, &end, 10);
+ if (pos == end) {
+ wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
+ return -1;
+ }
+
+ settings->freq_params.freq = atoi(end);
+ if (settings->freq_params.freq == 0) {
+ wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+ return -1;
+ }
+
+#define SET_CSA_SETTING(str) \
+ do { \
+ const char *pos2 = os_strstr(pos, " " #str "="); \
+ if (pos2) { \
+ pos2 += sizeof(" " #str "=") - 1; \
+ settings->freq_params.str = atoi(pos2); \
+ } \
+ } while (0)
+
+#define SET_CSA_SETTING_EXT(str) \
+ do { \
+ const char *pos2 = os_strstr(pos, " " #str "="); \
+ if (pos2) { \
+ pos2 += sizeof(" " #str "=") - 1; \
+ settings->str = atoi(pos2); \
+ } \
+ } while (0)
+
+ SET_CSA_SETTING(center_freq1);
+ SET_CSA_SETTING(center_freq2);
+ SET_CSA_SETTING(bandwidth);
+ SET_CSA_SETTING(sec_channel_offset);
+ SET_CSA_SETTING_EXT(punct_bitmap);
+ settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
+ settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+ settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
+ settings->block_tx = !!os_strstr(pos, " blocktx");
+#undef SET_CSA_SETTING
+#undef SET_CSA_SETTING_EXT
+
+ return 0;
+}
+
+
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ int i;
+
+ for (i = 0; i < iface->num_bss; i++)
+ hostapd_drv_stop_ap(iface->bss[i]);
+
+ return 0;
+}
+
+
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+ size_t len)
+{
+ return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
+}
+
+
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
+{
+ wpa_auth_pmksa_flush(hapd->wpa_auth);
+}
+
+
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
+{
+ u8 spa[ETH_ALEN];
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ char *pos, *pos2;
+ int akmp = 0, expiration = 0;
+
+ /*
+ * Entry format:
+ * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
+ */
+
+ if (hwaddr_aton(cmd, spa))
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+
+ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (!pos2)
+ return -1;
+ pmk_len = (pos2 - pos) / 2;
+ if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
+ hexstr2bin(pos, pmk, pmk_len) < 0)
+ return -1;
+
+ pos = pos2 + 1;
+
+ if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
+ return -1;
+
+ return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+ pmkid, expiration, akmp);
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+ const u8 *addr, char *buf, size_t len)
+{
+ return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
+}
+
+
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
+{
+ u8 spa[ETH_ALEN];
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN_MAX];
+ char *pos;
+ int expiration;
+
+ /*
+ * Entry format:
+ * <BSSID> <PMKID> <PMK> <expiration in seconds>
+ */
+
+ if (hwaddr_aton(cmd, spa))
+ return NULL;
+
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ return NULL;
+ pos++;
+
+ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+ return NULL;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return NULL;
+ pos++;
+
+ if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
+ return NULL;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return NULL;
+ pos++;
+
+ if (sscanf(pos, "%d", &expiration) != 1)
+ return NULL;
+
+ return wpa_auth_pmksa_create_entry(aa, spa, pmk, PMK_LEN,
+ WPA_KEY_MGMT_SAE, pmkid, expiration);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+#ifdef CONFIG_WNM_AP
+
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ int disassoc_timer;
+ struct sta_info *sta;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ if (cmd[17] != ' ')
+ return -1;
+ disassoc_timer = atoi(cmd + 17);
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for disassociation imminent message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *url, *timerstr;
+ int disassoc_timer;
+ struct sta_info *sta;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for ESS disassociation imminent message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ timerstr = cmd + 17;
+ if (*timerstr != ' ')
+ return -1;
+ timerstr++;
+ disassoc_timer = atoi(timerstr);
+ if (disassoc_timer < 0 || disassoc_timer > 65535)
+ return -1;
+
+ url = os_strchr(timerstr, ' ');
+ if (url == NULL)
+ return -1;
+ url++;
+
+ return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *pos, *end;
+ int disassoc_timer = 0;
+ struct sta_info *sta;
+ u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
+ u8 bss_term_dur[12];
+ char *url = NULL;
+ int ret;
+ u8 nei_rep[1000];
+ int nei_len;
+ u8 mbo[10];
+ size_t mbo_len = 0;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for BSS TM Request message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " disassoc_timer=");
+ if (pos) {
+ pos += 16;
+ disassoc_timer = atoi(pos);
+ if (disassoc_timer < 0 || disassoc_timer > 65535) {
+ wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+ return -1;
+ }
+ }
+
+ pos = os_strstr(cmd, " valid_int=");
+ if (pos) {
+ pos += 11;
+ valid_int = atoi(pos);
+ }
+
+ pos = os_strstr(cmd, " dialog_token=");
+ if (pos) {
+ pos += 14;
+ dialog_token = atoi(pos);
+ }
+
+ pos = os_strstr(cmd, " bss_term=");
+ if (pos) {
+ pos += 10;
+ req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+ /* TODO: TSF configurable/learnable */
+ bss_term_dur[0] = 4; /* Subelement ID */
+ bss_term_dur[1] = 10; /* Length */
+ os_memset(&bss_term_dur[2], 0, 8);
+ end = os_strchr(pos, ',');
+ if (end == NULL) {
+ wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+ return -1;
+ }
+ end++;
+ WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+ }
+
+ nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+ sizeof(nei_rep));
+ if (nei_len < 0)
+ return -1;
+
+ pos = os_strstr(cmd, " url=");
+ if (pos) {
+ size_t len;
+ pos += 5;
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ url = os_malloc(len + 1);
+ if (url == NULL)
+ return -1;
+ os_memcpy(url, pos, len);
+ url[len] = '\0';
+ req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ }
+
+ if (os_strstr(cmd, " pref=1"))
+ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+ if (os_strstr(cmd, " abridged=1"))
+ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+ if (os_strstr(cmd, " disassoc_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+ pos = os_strstr(cmd, "mbo=");
+ if (pos) {
+ unsigned int mbo_reason, cell_pref, reassoc_delay;
+ u8 *mbo_pos = mbo;
+
+ ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
+ &reassoc_delay, &cell_pref);
+ if (ret != 3) {
+ wpa_printf(MSG_DEBUG,
+ "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
+ ret = -1;
+ goto fail;
+ }
+
+ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid MBO transition reason code %u",
+ mbo_reason);
+ ret = -1;
+ goto fail;
+ }
+
+ /* Valid values for Cellular preference are: 0, 1, 255 */
+ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid MBO cellular capability %u",
+ cell_pref);
+ ret = -1;
+ goto fail;
+ }
+
+ if (reassoc_delay > 65535 ||
+ (reassoc_delay &&
+ !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
+ wpa_printf(MSG_DEBUG,
+ "MBO: Assoc retry delay is only valid in disassoc imminent mode");
+ ret = -1;
+ goto fail;
+ }
+
+ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = mbo_reason;
+ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = cell_pref;
+
+ if (reassoc_delay) {
+ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+ *mbo_pos++ = 2;
+ WPA_PUT_LE16(mbo_pos, reassoc_delay);
+ mbo_pos += 2;
+ }
+
+ mbo_len = mbo_pos - mbo;
+ }
+#endif /* CONFIG_MBO */
+
+ ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+ valid_int, bss_term_dur, dialog_token, url,
+ nei_len ? nei_rep : NULL, nei_len,
+ mbo_len ? mbo : NULL, mbo_len);
+#ifdef CONFIG_MBO
+fail:
+#endif /* CONFIG_MBO */
+ os_free(url);
+ return ret;
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct vlan_description vlan_id;
+
+ if (!(*num))
+ return 0;
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
+ hostapd_remove_acl_mac(acl, num, addr);
+
+ return 0;
+}
+
+
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+ int *num)
+{
+ while (*num)
+ hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
+}
+
+
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+ char *buf, size_t buflen)
+{
+ int i = 0, len = 0, ret = 0;
+
+ if (!acl)
+ return 0;
+
+ while (i < num) {
+ ret = os_snprintf(buf + len, buflen - len,
+ MACSTR " VLAN_ID=%d\n",
+ MAC2STR(acl[i].addr),
+ acl[i].vlan_id.untagged);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ i++;
+ len += ret;
+ }
+ return len;
+}
+
+
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct vlan_description vlan_id;
+ int ret = 0, vlanid = 0;
+ const char *pos;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = os_strstr(cmd, "VLAN_ID=");
+ if (pos)
+ vlanid = atoi(pos + 8);
+
+ if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
+ ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
+ if (ret != -1 && *acl)
+ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+ }
+
+ return ret < 0 ? -1 : 0;
+}
+
+
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct vlan_description vlan_id;
+
+ if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
+ return 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac,
+ sta->addr, &vlan_id) ||
+ (vlan_id.notempty &&
+ vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+
+ return 0;
+}
+
+
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct vlan_description vlan_id;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, sta->addr,
+ &vlan_id) &&
+ (!vlan_id.notempty ||
+ !vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.h
new file mode 100644
index 0000000..614f042
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.h
@@ -0,0 +1,57 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_AP_H
+#define CTRL_IFACE_AP_H
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+ char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+ char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ char *buf, size_t buflen);
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+ const char *txtaddr);
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+ const char *txtaddr);
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+ const char *txtaddr,
+ char *buf, size_t buflen);
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+ const char *txtaddr);
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen);
+int hostapd_parse_csa_settings(const char *pos,
+ struct csa_settings *settings);
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+ size_t len);
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+ const u8 *addr, char *buf, size_t len);
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
+
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+ const char *cmd);
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+ const char *cmd);
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ const char *cmd);
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+ const char *cmd);
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+ const char *txtaddr);
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+ int *num);
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+ char *buf, size_t buflen);
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd);
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd);
+
+#endif /* CTRL_IFACE_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.c
new file mode 100644
index 0000000..d1786ee
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.c
@@ -0,0 +1,1669 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+#include "crypto/crypto.h"
+
+
+enum dfs_channel_type {
+ DFS_ANY_CHANNEL,
+ DFS_AVAILABLE, /* non-radar or radar-available */
+ DFS_NO_CAC_YET, /* radar-not-yet-available */
+};
+
+static struct hostapd_channel_data *
+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx,
+ enum dfs_channel_type *channel_type);
+
+
+static bool dfs_use_radar_background(struct hostapd_iface *iface)
+{
+ return (iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) &&
+ iface->conf->enable_background_radar;
+}
+
+
+static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+{
+ int n_chans = 1;
+
+ *seg1 = 0;
+
+ if (iface->conf->ieee80211n && iface->conf->secondary_channel)
+ n_chans = 2;
+
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ n_chans = 4;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ n_chans = 4;
+ *seg1 = 4;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return n_chans;
+}
+
+
+/* dfs_channel_available: select new channel according to type parameter */
+static int dfs_channel_available(struct hostapd_channel_data *chan,
+ enum dfs_channel_type type)
+{
+ if (type == DFS_NO_CAC_YET) {
+ /* Select only radar channel where CAC has not been
+ * performed yet
+ */
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_USABLE)
+ return 1;
+ return 0;
+ }
+
+ /*
+ * When radar detection happens, CSA is performed. However, there's no
+ * time for CAC, so radar channels must be skipped when finding a new
+ * channel for CSA, unless they are available for immediate use.
+ */
+ if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+ HOSTAPD_CHAN_DFS_AVAILABLE))
+ return 0;
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return 0;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_UNAVAILABLE))
+ return 0;
+ return 1;
+}
+
+
+static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
+{
+ /*
+ * The tables contain first valid channel number based on channel width.
+ * We will also choose this first channel as the control one.
+ */
+ int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+ 165, 173, 184, 192 };
+ /*
+ * VHT80, valid channels based on center frequency:
+ * 42, 58, 106, 122, 138, 155, 171
+ */
+ int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 };
+ /*
+ * VHT160 valid channels based on center frequency:
+ * 50, 114, 163
+ */
+ int allowed_160[] = { 36, 100, 149 };
+ int *allowed = allowed_40;
+ unsigned int i, allowed_no = 0;
+
+ switch (n_chans) {
+ case 2:
+ allowed = allowed_40;
+ allowed_no = ARRAY_SIZE(allowed_40);
+ break;
+ case 4:
+ allowed = allowed_80;
+ allowed_no = ARRAY_SIZE(allowed_80);
+ break;
+ case 8:
+ allowed = allowed_160;
+ allowed_no = ARRAY_SIZE(allowed_160);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
+ break;
+ }
+
+ for (i = 0; i < allowed_no; i++) {
+ if (chan->chan == allowed[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static struct hostapd_channel_data *
+dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
+{
+ int i;
+
+ for (i = first_chan_idx; i < mode->num_channels; i++) {
+ if (mode->channels[i].freq == freq)
+ return &mode->channels[i];
+ }
+
+ return NULL;
+}
+
+
+static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ int first_chan_idx, int num_chans,
+ enum dfs_channel_type type)
+{
+ struct hostapd_channel_data *first_chan, *chan;
+ int i;
+ u32 bw = num_chan_to_bw(num_chans);
+
+ if (first_chan_idx + num_chans > mode->num_channels) {
+ wpa_printf(MSG_DEBUG,
+ "DFS: some channels in range not defined");
+ return 0;
+ }
+
+ first_chan = &mode->channels[first_chan_idx];
+
+ /* hostapd DFS implementation assumes the first channel as primary.
+ * If it's not allowed to use the first channel as primary, decline the
+ * whole channel range. */
+ if (!chan_pri_allowed(first_chan)) {
+ wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed");
+ return 0;
+ }
+
+ for (i = 0; i < num_chans; i++) {
+ chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
+ first_chan_idx);
+ if (!chan) {
+ wpa_printf(MSG_DEBUG, "DFS: no channel data for %d",
+ first_chan->freq + i * 20);
+ return 0;
+ }
+
+ /* HT 40 MHz secondary channel availability checked only for
+ * primary channel */
+ if (!chan_bw_allowed(chan, bw, 1, !i)) {
+ wpa_printf(MSG_DEBUG, "DFS: bw now allowed for %d",
+ first_chan->freq + i * 20);
+ return 0;
+ }
+
+ if (!dfs_channel_available(chan, type)) {
+ wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
+ first_chan->freq + i * 20);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ if (!iface->conf->acs_ch_list.num)
+ return 1;
+
+ return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+/*
+ * The function assumes HT40+ operation.
+ * Make sure to adjust the following variables after calling this:
+ * - hapd->secondary_channel
+ * - hapd->vht/he_oper_centr_freq_seg0_idx
+ * - hapd->vht/he_oper_centr_freq_seg1_idx
+ */
+static int dfs_find_channel(struct hostapd_iface *iface,
+ struct hostapd_channel_data **ret_chan,
+ int idx, enum dfs_channel_type type)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int i, channel_idx = 0, n_chans, n_chans1;
+
+ mode = iface->current_mode;
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ /* Skip HT40/VHT incompatible channels */
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel &&
+ (!dfs_is_chan_allowed(chan, n_chans) ||
+ !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
+ wpa_printf(MSG_DEBUG,
+ "DFS: channel %d (%d) is incompatible",
+ chan->freq, chan->chan);
+ continue;
+ }
+
+ /* Skip incompatible chandefs */
+ if (!dfs_chan_range_available(mode, i, n_chans, type)) {
+ wpa_printf(MSG_DEBUG,
+ "DFS: range not available for %d (%d)",
+ chan->freq, chan->chan);
+ continue;
+ }
+
+ if (!is_in_chanlist(iface, chan)) {
+ wpa_printf(MSG_DEBUG,
+ "DFS: channel %d (%d) not in chanlist",
+ chan->freq, chan->chan);
+ continue;
+ }
+
+ if (chan->max_tx_power < iface->conf->min_tx_power)
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
+ if (ret_chan && idx == channel_idx) {
+ wpa_printf(MSG_DEBUG, "Selected channel %d (%d)",
+ chan->freq, chan->chan);
+ *ret_chan = chan;
+ return idx;
+ }
+ wpa_printf(MSG_DEBUG, "Adding channel %d (%d)",
+ chan->freq, chan->chan);
+ channel_idx++;
+ }
+ return channel_idx;
+}
+
+
+static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan,
+ int secondary_channel,
+ int sec_chan_idx_80p80,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx)
+{
+ if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+ return;
+
+ if (!chan)
+ return;
+
+ *oper_centr_freq_seg1_idx = 0;
+
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+ if (secondary_channel == 1)
+ *oper_centr_freq_seg0_idx = chan->chan + 2;
+ else if (secondary_channel == -1)
+ *oper_centr_freq_seg0_idx = chan->chan - 2;
+ else
+ *oper_centr_freq_seg0_idx = chan->chan;
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ *oper_centr_freq_seg0_idx = chan->chan + 6;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ *oper_centr_freq_seg0_idx = chan->chan + 14;
+ break;
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ *oper_centr_freq_seg0_idx = chan->chan + 6;
+ *oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6;
+ break;
+
+ default:
+ wpa_printf(MSG_INFO,
+ "DFS: Unsupported channel width configuration");
+ *oper_centr_freq_seg0_idx = 0;
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
+ *oper_centr_freq_seg0_idx,
+ *oper_centr_freq_seg1_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int channel_no = iface->conf->channel;
+ int res = -1, i;
+ int chan_seg1 = -1;
+
+ *seg1_start = -1;
+
+ /* HT40- */
+ if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
+ channel_no -= 4;
+
+ /* VHT/HE */
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ switch (hostapd_get_oper_chwidth(iface->conf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+ iface->conf) - 6;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+ iface->conf) - 14;
+ break;
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+ iface->conf) - 6;
+ chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
+ iface->conf) - 6;
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "DFS only VHT20/40/80/160/80+80 is supported now");
+ channel_no = -1;
+ break;
+ }
+ }
+
+ /* Get idx */
+ mode = iface->current_mode;
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->chan == channel_no) {
+ res = i;
+ break;
+ }
+ }
+
+ if (res != -1 && chan_seg1 > -1) {
+ int found = 0;
+
+ /* Get idx for seg1 */
+ mode = iface->current_mode;
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->chan == chan_seg1) {
+ *seg1_start = i;
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ res = -1;
+ }
+
+ if (res == -1) {
+ wpa_printf(MSG_DEBUG,
+ "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
+ mode->num_channels, channel_no, iface->conf->channel,
+ iface->conf->ieee80211n,
+ iface->conf->secondary_channel,
+ hostapd_get_oper_chwidth(iface->conf));
+
+ for (i = 0; i < mode->num_channels; i++) {
+ wpa_printf(MSG_DEBUG, "Available channel: %d",
+ mode->channels[i].chan);
+ }
+ }
+
+ return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i, res = 0;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ if (start_chan_idx + i >= mode->num_channels)
+ break;
+ channel = &mode->channels[start_chan_idx + i];
+ if (channel->flag & HOSTAPD_CHAN_RADAR)
+ res++;
+ }
+
+ return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+
+ if (channel->flag & HOSTAPD_CHAN_DISABLED)
+ break;
+
+ if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+
+ if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+ HOSTAPD_CHAN_DFS_AVAILABLE)
+ break;
+ }
+
+ return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
+ int start_chan_idx,
+ int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i, res = 0;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if (channel->flag & HOSTAPD_CHAN_DISABLED)
+ res++;
+ if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_UNAVAILABLE)
+ res++;
+ }
+
+ return res;
+}
+
+
+static struct hostapd_channel_data *
+dfs_get_valid_channel(struct hostapd_iface *iface,
+ int *secondary_channel,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx,
+ enum dfs_channel_type type)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ struct hostapd_channel_data *chan2 = NULL;
+ int num_available_chandefs;
+ int chan_idx, chan_idx2;
+ int sec_chan_idx_80p80 = -1;
+ bool is_mesh = false;
+ int i;
+ u32 _rand;
+
+#ifdef CONFIG_MESH
+ is_mesh = iface->mconf;
+#endif
+
+ wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+ *secondary_channel = 0;
+ *oper_centr_freq_seg0_idx = 0;
+ *oper_centr_freq_seg1_idx = 0;
+
+ if (iface->current_mode == NULL)
+ return NULL;
+
+ mode = iface->current_mode;
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return NULL;
+
+ /* Get the count first */
+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
+ wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
+ num_available_chandefs);
+ if (num_available_chandefs == 0)
+ return NULL;
+
+ /* try to use deterministic channel in mesh, so that both sides
+ * have a chance to switch to the same channel */
+ if (is_mesh) {
+#ifdef CONFIG_MESH
+ u64 hash[4];
+ const u8 *meshid[1] = { &iface->mconf->meshid[0] };
+ const size_t meshid_len = iface->mconf->meshid_len;
+
+ sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
+ _rand = hash[0] + hash[1] + hash[2] + hash[3];
+#endif
+ } else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ return NULL;
+
+ chan_idx = _rand % num_available_chandefs;
+ dfs_find_channel(iface, &chan, chan_idx, type);
+ if (!chan) {
+ wpa_printf(MSG_DEBUG, "DFS: no random channel found");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DFS: got random channel %d (%d)",
+ chan->freq, chan->chan);
+
+ /* dfs_find_channel() calculations assume HT40+ */
+ if (iface->conf->secondary_channel)
+ *secondary_channel = 1;
+ else
+ *secondary_channel = 0;
+
+ /* Get secondary channel for HT80P80 */
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_80P80MHZ) {
+ if (num_available_chandefs <= 1) {
+ wpa_printf(MSG_ERROR,
+ "only 1 valid chan, can't support 80+80");
+ return NULL;
+ }
+
+ /*
+ * Loop all channels except channel1 to find a valid channel2
+ * that is not adjacent to channel1.
+ */
+ for (i = 0; i < num_available_chandefs - 1; i++) {
+ /* start from chan_idx + 1, end when chan_idx - 1 */
+ chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
+ dfs_find_channel(iface, &chan2, chan_idx2, type);
+ if (chan2 && abs(chan2->chan - chan->chan) > 12) {
+ /* two channels are not adjacent */
+ sec_chan_idx_80p80 = chan2->chan;
+ wpa_printf(MSG_DEBUG,
+ "DFS: got second chan: %d (%d)",
+ chan2->freq, chan2->chan);
+ break;
+ }
+ }
+
+ /* Check if we got a valid secondary channel which is not
+ * adjacent to the first channel.
+ */
+ if (sec_chan_idx_80p80 == -1) {
+ wpa_printf(MSG_INFO,
+ "DFS: failed to get chan2 for 80+80");
+ return NULL;
+ }
+ }
+
+ dfs_adjust_center_freq(iface, chan,
+ *secondary_channel,
+ sec_chan_idx_80p80,
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx);
+
+ return chan;
+}
+
+
+static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+{
+ struct hostapd_channel_data *channel;
+ u8 cf1 = 0, cf2 = 0;
+ int sec = 0;
+
+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+ skip_radar ? DFS_AVAILABLE :
+ DFS_ANY_CHANNEL);
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "could not get valid channel");
+ return -1;
+ }
+
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = sec;
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+
+ return 0;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ int i;
+
+ mode = iface->current_mode;
+ if (mode == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->freq == freq) {
+ if (chan->flag & HOSTAPD_CHAN_RADAR) {
+ chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+ chan->flag |= state;
+ return 1; /* Channel found */
+ }
+ }
+ }
+ wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+ return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
+ int chan_offset, int chan_width, int cf1,
+ int cf2, u32 state)
+{
+ int n_chans = 1, i;
+ struct hostapd_hw_modes *mode;
+ int frequency = freq;
+ int frequency2 = 0;
+ int ret = 0;
+
+ mode = iface->current_mode;
+ if (mode == NULL)
+ return 0;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+ wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+ return 0;
+ }
+
+ /* Seems cf1 and chan_width is enough here */
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ n_chans = 1;
+ if (frequency == 0)
+ frequency = cf1;
+ break;
+ case CHAN_WIDTH_40:
+ n_chans = 2;
+ frequency = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ n_chans = 4;
+ frequency = cf1 - 30;
+ break;
+ case CHAN_WIDTH_80P80:
+ n_chans = 4;
+ frequency = cf1 - 30;
+ frequency2 = cf2 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ n_chans = 8;
+ frequency = cf1 - 70;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+ chan_width);
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+ n_chans);
+ for (i = 0; i < n_chans; i++) {
+ ret += set_dfs_state_freq(iface, frequency, state);
+ frequency = frequency + 20;
+
+ if (chan_width == CHAN_WIDTH_80P80) {
+ ret += set_dfs_state_freq(iface, frequency2, state);
+ frequency2 = frequency2 + 20;
+ }
+ }
+
+ return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+ int chan_width, int cf1, int cf2)
+{
+ int start_chan_idx, start_chan_idx1;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
+ u8 radar_chan;
+ int res = 0;
+
+ /* Our configuration */
+ mode = iface->current_mode;
+ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Check we are on DFS channel(s) */
+ if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
+ return 0;
+
+ /* Reported via radar event */
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ radar_n_chans = 1;
+ if (frequency == 0)
+ frequency = cf1;
+ break;
+ case CHAN_WIDTH_40:
+ radar_n_chans = 2;
+ frequency = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ radar_n_chans = 4;
+ frequency = cf1 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ radar_n_chans = 8;
+ frequency = cf1 - 70;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+ chan_width);
+ break;
+ }
+
+ ieee80211_freq_to_chan(frequency, &radar_chan);
+
+ for (i = 0; i < n_chans; i++) {
+ chan = &mode->channels[start_chan_idx + i];
+ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+ for (j = 0; j < radar_n_chans; j++) {
+ wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+ chan->chan, radar_chan + j * 4);
+ if (chan->chan == radar_chan + j * 4)
+ res++;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+ return res;
+}
+
+
+static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i;
+ unsigned int cac_time_ms = 0;
+#ifdef CONFIG_DFS_CAC_TIMER
+ /* Yewei, add dfs cac timer in seconds,, 20240118 */
+ if (iface->conf->dfs_cac_s) {
+ cac_time_ms = iface->conf->dfs_cac_s * 1000;
+ return cac_time_ms;
+ }
+#endif /* CONFIG_DFS_CAC_TIMER */
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ if (start_chan_idx + i >= mode->num_channels)
+ break;
+ channel = &mode->channels[start_chan_idx + i];
+ if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+ if (channel->dfs_cac_ms > cac_time_ms)
+ cac_time_ms = channel->dfs_cac_ms;
+ }
+
+ return cac_time_ms;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_iface *iface)
+{
+ int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+ int skip_radar = 0;
+
+ if (is_6ghz_freq(iface->freq))
+ return 1;
+
+ if (!iface->current_mode) {
+ /*
+ * This can happen with drivers that do not provide mode
+ * information and as such, cannot really use hostapd for DFS.
+ */
+ wpa_printf(MSG_DEBUG,
+ "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
+ return 1;
+ }
+
+ iface->cac_started = 0;
+
+ do {
+ /* Get start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(iface,
+ &start_chan_idx1);
+ if (start_chan_idx == -1)
+ return -1;
+
+ /* Get number of used channels, depend on width */
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Setup CAC time */
+ iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
+ n_chans);
+
+ /* Check if any of configured channels require DFS */
+ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+ wpa_printf(MSG_DEBUG,
+ "DFS %d channels required radar detection",
+ res);
+ if (!res)
+ return 1;
+
+ /* Check if all channels are DFS available */
+ res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
+ wpa_printf(MSG_DEBUG,
+ "DFS all channels available, (SKIP CAC): %s",
+ res ? "yes" : "no");
+ if (res)
+ return 1;
+
+ /* Check if any of configured channels is unavailable */
+ res = dfs_check_chans_unavailable(iface, start_chan_idx,
+ n_chans);
+ wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+ res, res ? "yes": "no");
+ if (res) {
+ if (dfs_set_valid_channel(iface, skip_radar) < 0) {
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ return 0;
+ }
+ }
+ } while (res);
+
+ /* Finally start CAC */
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+ dfs_use_radar_background(iface) ? " (background)" : "");
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+ iface->freq,
+ iface->conf->channel, iface->conf->secondary_channel,
+ hostapd_get_oper_chwidth(iface->conf),
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ iface->dfs_cac_ms / 1000);
+
+ res = hostapd_start_dfs_cac(
+ iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+ iface->conf->ieee80211n, iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax, iface->conf->ieee80211be,
+ iface->conf->secondary_channel,
+ hostapd_get_oper_chwidth(iface->conf),
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ dfs_use_radar_background(iface));
+
+ if (res) {
+ wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ return -1;
+ }
+
+ if (dfs_use_radar_background(iface)) {
+ /* Cache background radar parameters. */
+ iface->radar_background.channel = iface->conf->channel;
+ iface->radar_background.secondary_channel =
+ iface->conf->secondary_channel;
+ iface->radar_background.freq = iface->freq;
+ iface->radar_background.centr_freq_seg0_idx =
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+ iface->radar_background.centr_freq_seg1_idx =
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+
+ /*
+ * Let's select a random channel according to the
+ * regulations and perform CAC on dedicated radar chain.
+ */
+ res = dfs_set_valid_channel(iface, 1);
+ if (res < 0)
+ return res;
+
+ iface->radar_background.temp_ch = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
+{
+ int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+
+ /* Get the start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+ if (start_chan_idx < 0)
+ return 0;
+
+ /* Get the number of used channels, depending on width */
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Check if all channels are DFS available */
+ return dfs_check_chans_available(iface, start_chan_idx, n_chans);
+}
+
+
+static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ int channel, int freq,
+ int secondary_channel,
+ u8 current_vht_oper_chwidth,
+ u8 oper_centr_freq_seg0_idx,
+ u8 oper_centr_freq_seg1_idx)
+{
+ struct hostapd_hw_modes *cmode = iface->current_mode;
+ int ieee80211_mode = IEEE80211_MODE_AP, err;
+ struct csa_settings csa_settings;
+ u8 new_vht_oper_chwidth;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+ "freq=%d chan=%d sec_chan=%d", freq, channel,
+ secondary_channel);
+
+ new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
+
+ /* Setup CSA request */
+ os_memset(&csa_settings, 0, sizeof(csa_settings));
+ csa_settings.cs_count = 5;
+ csa_settings.block_tx = 1;
+#ifdef CONFIG_MESH
+ if (iface->mconf)
+ ieee80211_mode = IEEE80211_MODE_MESH;
+#endif /* CONFIG_MESH */
+ err = hostapd_set_freq_params(&csa_settings.freq_params,
+ iface->conf->hw_mode,
+ freq, channel,
+ iface->conf->enable_edmg,
+ iface->conf->edmg_channel,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax,
+ iface->conf->ieee80211be,
+ secondary_channel,
+ new_vht_oper_chwidth,
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx,
+ cmode->vht_capab,
+ &cmode->he_capab[ieee80211_mode],
+ &cmode->eht_capab[ieee80211_mode]);
+
+ if (err) {
+ wpa_printf(MSG_ERROR,
+ "DFS failed to calculate CSA freq params");
+ hostapd_disable_iface(iface);
+ return err;
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+ if (err)
+ break;
+ }
+
+ if (err) {
+ wpa_printf(MSG_WARNING,
+ "DFS failed to schedule CSA (%d) - trying fallback",
+ err);
+ iface->freq = freq;
+ iface->conf->channel = channel;
+ iface->conf->secondary_channel = secondary_channel;
+ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+ oper_centr_freq_seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ oper_centr_freq_seg1_idx);
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+
+ return 0;
+ }
+
+ /* Channel configuration will be updated once CSA completes and
+ * ch_switch_notify event is received */
+ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+
+ return 0;
+}
+
+
+static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+{
+ int sec = 0;
+ enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+ struct hostapd_channel_data *channel;
+ u8 oper_centr_freq_seg0_idx = 0;
+ u8 oper_centr_freq_seg1_idx = 0;
+
+ /*
+ * Allow selection of DFS channel in ETSI to comply with
+ * uniform spreading.
+ */
+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ channel_type = DFS_ANY_CHANNEL;
+
+ channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ channel_type);
+ if (!channel ||
+ channel->chan == iface->conf->channel ||
+ channel->chan == iface->radar_background.channel)
+ channel = dfs_downgrade_bandwidth(iface, &sec,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ &channel_type);
+ if (!channel ||
+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ channel->freq, channel->chan,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax,
+ iface->conf->ieee80211be,
+ sec, hostapd_get_oper_chwidth(iface->conf),
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx, true)) {
+ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
+ iface->radar_background.channel = -1;
+ return;
+ }
+
+ iface->radar_background.channel = channel->chan;
+ iface->radar_background.freq = channel->freq;
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: setting background chain to chan %d (%d MHz)",
+ __func__, channel->chan, channel->freq);
+}
+
+
+static bool
+hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq)
+{
+ return dfs_use_radar_background(iface) &&
+ iface->radar_background.channel != -1 &&
+ iface->radar_background.freq == freq;
+}
+
+
+static int
+hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+{
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+ iface->conf->channel = iface->radar_background.channel;
+ iface->freq = iface->radar_background.freq;
+ iface->conf->secondary_channel =
+ iface->radar_background.secondary_channel;
+ hostapd_set_oper_centr_freq_seg0_idx(
+ iface->conf, iface->radar_background.centr_freq_seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(
+ iface->conf, iface->radar_background.centr_freq_seg1_idx);
+
+ hostpad_dfs_update_background_chain(iface);
+
+ return hostapd_dfs_request_channel_switch(
+ iface, iface->conf->channel, iface->freq,
+ iface->conf->secondary_channel, current_vht_oper_chwidth,
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+ "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ if (success) {
+ /* Complete iface/ap configuration */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+ /* Complete AP configuration for the first bring up. */
+ if (iface->state != HAPD_IFACE_ENABLED)
+ hostapd_setup_interface_complete(iface, 0);
+ else
+ iface->cac_started = 0;
+ } else {
+ set_dfs_state(iface, freq, ht_enabled, chan_offset,
+ chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_AVAILABLE);
+
+ /*
+ * Radar event from background chain for the selected
+ * channel. Perform CSA, move the main chain to the
+ * selected channel and configure the background chain
+ * to a new DFS channel.
+ */
+ if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
+ if (!iface->radar_background.temp_ch)
+ return 0;
+
+ iface->radar_background.temp_ch = 0;
+ return hostapd_dfs_start_channel_switch_background(iface);
+ }
+
+ /*
+ * Just mark the channel available when CAC completion
+ * event is received in enabled state. CAC result could
+ * have been propagated from another radio having the
+ * same regulatory configuration. When CAC completion is
+ * received during non-HAPD_IFACE_ENABLED state, make
+ * sure the configured channel is available because this
+ * CAC completion event could have been propagated from
+ * another radio.
+ */
+ if (iface->state != HAPD_IFACE_ENABLED &&
+ hostapd_is_dfs_chan_available(iface)) {
+ hostapd_setup_interface_complete(iface, 0);
+ iface->cac_started = 0;
+ }
+ }
+ } else if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
+ hostpad_dfs_update_background_chain(iface);
+ }
+
+ return 0;
+}
+
+
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
+
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+
+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+ return 0;
+}
+
+
+static struct hostapd_channel_data *
+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx,
+ enum dfs_channel_type *channel_type)
+{
+ struct hostapd_channel_data *channel;
+
+ for (;;) {
+ channel = dfs_get_valid_channel(iface, secondary_channel,
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx,
+ *channel_type);
+ if (channel) {
+ wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d",
+ channel->chan);
+ return channel;
+ }
+
+ if (*channel_type != DFS_ANY_CHANNEL) {
+ *channel_type = DFS_ANY_CHANNEL;
+ } else {
+ int oper_chwidth;
+
+ oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ if (oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+ break;
+ *channel_type = DFS_AVAILABLE;
+ hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
+ }
+ }
+
+ wpa_printf(MSG_INFO,
+ "%s: no DFS channels left, waiting for NOP to finish",
+ __func__);
+ return NULL;
+}
+
+
+static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *channel;
+ int secondary_channel;
+ u8 oper_centr_freq_seg0_idx = 0;
+ u8 oper_centr_freq_seg1_idx = 0;
+ enum dfs_channel_type channel_type = DFS_ANY_CHANNEL;
+ int err = 1;
+
+ /* Radar detected during active CAC */
+ iface->cac_started = 0;
+ channel = dfs_get_valid_channel(iface, &secondary_channel,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ channel_type);
+
+ if (!channel) {
+ channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ &channel_type);
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "No valid channel available");
+ return err;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+ channel->chan);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+ "freq=%d chan=%d sec_chan=%d", channel->freq,
+ channel->chan, secondary_channel);
+
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = secondary_channel;
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+ oper_centr_freq_seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ oper_centr_freq_seg1_idx);
+ err = 0;
+
+ hostapd_setup_interface_complete(iface, err);
+ return err;
+}
+
+
+static int
+hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ int freq)
+{
+ if (!dfs_use_radar_background(iface))
+ return -1; /* Background radar chain not supported. */
+
+ wpa_printf(MSG_DEBUG,
+ "%s called (background CAC active: %s, CSA active: %s)",
+ __func__, iface->radar_background.cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+
+ if (hostapd_dfs_is_background_event(iface, freq)) {
+ /*
+ * Radar pattern is reported on the background chain.
+ * Just select a new random channel according to the
+ * regulations for monitoring.
+ */
+ hostpad_dfs_update_background_chain(iface);
+ return 0;
+ }
+
+ /*
+ * If background radar detection is supported and the radar channel
+ * monitored by the background chain is available switch to it without
+ * waiting for the CAC.
+ */
+ if (iface->radar_background.channel == -1)
+ return -1; /* Background radar chain not available. */
+
+ if (iface->radar_background.cac_started) {
+ /*
+ * Background channel not available yet. Perform CAC on the
+ * main chain.
+ */
+ iface->radar_background.temp_ch = 1;
+ return -1;
+ }
+
+ return hostapd_dfs_start_channel_switch_background(iface);
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *channel;
+ int secondary_channel;
+ u8 oper_centr_freq_seg0_idx;
+ u8 oper_centr_freq_seg1_idx;
+ enum dfs_channel_type channel_type = DFS_AVAILABLE;
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+ wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
+ __func__, iface->cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+
+ /* Check if active CAC */
+ if (iface->cac_started)
+ return hostapd_dfs_start_channel_switch_cac(iface);
+
+ /*
+ * Allow selection of DFS channel in ETSI to comply with
+ * uniform spreading.
+ */
+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ channel_type = DFS_ANY_CHANNEL;
+
+ /* Perform channel switch/CSA */
+ channel = dfs_get_valid_channel(iface, &secondary_channel,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ channel_type);
+
+ if (!channel) {
+ /*
+ * If there is no channel to switch immediately to, check if
+ * there is another channel where we can switch even if it
+ * requires to perform a CAC first.
+ */
+ channel_type = DFS_ANY_CHANNEL;
+ channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ &channel_type);
+ if (!channel) {
+ /*
+ * Toggle interface state to enter DFS state
+ * until NOP is finished.
+ */
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return 0;
+ }
+
+ if (channel_type == DFS_ANY_CHANNEL) {
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = secondary_channel;
+ hostapd_set_oper_centr_freq_seg0_idx(
+ iface->conf, oper_centr_freq_seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(
+ iface->conf, oper_centr_freq_seg1_idx);
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return 0;
+ }
+ }
+
+ return hostapd_dfs_request_channel_switch(iface, channel->chan,
+ channel->freq,
+ secondary_channel,
+ current_vht_oper_chwidth,
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx);
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+
+ if (!iface->conf->ieee80211h)
+ return 0;
+
+ /* mark radar frequency as invalid */
+ if (!set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE))
+ return 0;
+
+ if (!hostapd_dfs_is_background_event(iface, freq)) {
+ /* Skip if reported radar event not overlapped our channels */
+ if (!dfs_are_channels_overlapped(iface, freq, chan_width,
+ cf1, cf2))
+ return 0;
+ }
+
+ if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+ /* Radar detected while operating, switch the channel. */
+ return hostapd_dfs_start_channel_switch(iface);
+ }
+
+ return 0;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+
+ /* TODO add correct implementation here */
+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+ if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) {
+ /* Handle cases where all channels were initially unavailable */
+ hostapd_handle_dfs(iface);
+ } else if (dfs_use_radar_background(iface) &&
+ iface->radar_background.channel == -1) {
+ /* Reset radar background chain if disabled */
+ hostpad_dfs_update_background_chain(iface);
+ }
+
+ return 0;
+}
+
+
+int hostapd_is_dfs_required(struct hostapd_iface *iface)
+{
+ int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+
+ if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ !iface->conf->ieee80211h) ||
+ !iface->current_mode ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ /* Get start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+ if (start_chan_idx == -1)
+ return -1;
+
+ /* Get number of used channels, depend on width */
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Check if any of configured channels require DFS */
+ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+ if (res)
+ return res;
+ if (start_chan_idx1 >= 0 && n_chans1 > 0)
+ res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
+ return res;
+}
+
+
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 1;
+ } else {
+ /* This is called when the driver indicates that an offloaded
+ * DFS has started CAC. */
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ iface->cac_started = 1;
+ }
+ /* TODO: How to check CAC time for ETSI weather channels? */
+#ifdef CONFIG_DFS_CAC_TIMER
+ /* Yewei, add dfs cac timer in seconds, 20240118 */
+ if (iface->conf->dfs_cac_s) {
+ iface->dfs_cac_ms = iface->conf->dfs_cac_s * 1000;
+ }
+ else
+#endif /* CONFIG_DFS_CAC_TIMER */
+ {
+ iface->dfs_cac_ms = 60000;
+ }
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
+ "seg1=%d cac_time=%ds%s",
+ freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
+ iface->dfs_cac_ms / 1000,
+ hostapd_dfs_is_background_event(iface, freq) ?
+ " (background)" : "");
+
+ os_get_reltime(&iface->dfs_cac_start);
+ return 0;
+}
+
+
+/*
+ * Main DFS handler for offloaded case.
+ * 2 - continue channel/AP setup for non-DFS channel
+ * 1 - continue channel/AP setup for DFS channel
+ * 0 - channel/AP setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+{
+ int dfs_res;
+
+ wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+ __func__, iface->cac_started);
+
+ /*
+ * If DFS has already been started, then we are being called from a
+ * callback to continue AP/channel setup. Reset the CAC start flag and
+ * return.
+ */
+ if (iface->cac_started) {
+ wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+ __func__, iface->cac_started);
+ iface->cac_started = 0;
+ return 1;
+ }
+
+ dfs_res = hostapd_is_dfs_required(iface);
+ if (dfs_res > 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: freq %d MHz requires DFS for %d chans",
+ __func__, iface->freq, dfs_res);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
+ __func__, iface->freq);
+ return 2;
+}
+
+
+int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ int center_freq)
+{
+ struct hostapd_channel_data *chan;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ int half_width;
+ int res = 0;
+ int i;
+
+ if (!iface->conf->ieee80211h || !mode ||
+ mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ switch (width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ half_width = 10;
+ break;
+ case CHAN_WIDTH_40:
+ half_width = 20;
+ break;
+ case CHAN_WIDTH_80:
+ case CHAN_WIDTH_80P80:
+ half_width = 40;
+ break;
+ case CHAN_WIDTH_160:
+ half_width = 80;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "DFS chanwidth %d not supported",
+ width);
+ return 0;
+ }
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+
+ if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_AVAILABLE)
+ continue;
+
+ if (center_freq - chan->freq < half_width &&
+ chan->freq - center_freq < half_width)
+ res++;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s",
+ center_freq - half_width, center_freq + half_width,
+ res ? "yes" : "no");
+
+ return res;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.h
new file mode 100644
index 0000000..606c1b3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.h
@@ -0,0 +1,36 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
+int hostapd_is_dfs_required(struct hostapd_iface *iface);
+int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+ int center_freq);
+
+#endif /* DFS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.c
new file mode 100644
index 0000000..551936b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.c
@@ -0,0 +1,160 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/dhcp.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+
+
+static const char * ipaddr_str(u32 addr)
+{
+ static char buf[17];
+
+ os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ return buf;
+}
+
+
+static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ const struct bootp_pkt *b;
+ struct sta_info *sta;
+ int exten_len;
+ const u8 *end, *pos;
+ int res, msgtype = 0, prefixlen = 32;
+ u32 subnet_mask = 0;
+ u16 ip_len;
+
+ exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
+ if (exten_len < 4)
+ return;
+
+ b = (const struct bootp_pkt *) &buf[ETH_HLEN];
+ ip_len = ntohs(b->iph.ip_len);
+ if (ip_len > (unsigned int) (len - ETH_HLEN))
+ return;
+
+ if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
+ return;
+
+ /* Parse DHCP options */
+ end = (const u8 *) b + ip_len;
+ pos = &b->exten[4];
+ while (pos < end && *pos != DHCP_OPT_END) {
+ const u8 *opt = pos++;
+
+ if (*opt == DHCP_OPT_PAD)
+ continue;
+
+ if (pos >= end || 1 + *pos > end - pos)
+ break;
+ pos += *pos + 1;
+ if (pos >= end)
+ break;
+
+ switch (*opt) {
+ case DHCP_OPT_SUBNET_MASK:
+ if (opt[1] == 4)
+ subnet_mask = WPA_GET_BE32(&opt[2]);
+ if (subnet_mask == 0)
+ return;
+ while (!(subnet_mask & 0x1)) {
+ subnet_mask >>= 1;
+ prefixlen--;
+ }
+ break;
+ case DHCP_OPT_MSG_TYPE:
+ if (opt[1])
+ msgtype = opt[2];
+ break;
+ default:
+ break;
+ }
+ }
+
+#ifdef CONFIG_HS20
+ if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ continue;
+ x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+ (u8 *) buf, len);
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ if (msgtype == DHCPACK) {
+ if (b->your_ip == 0)
+ return;
+
+ /* DHCPACK for DHCPREQUEST */
+ sta = ap_get_sta(hapd, b->hw_addr);
+ if (!sta)
+ return;
+
+ wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+ " @ IPv4 address %s/%d",
+ MAC2STR(sta->addr),
+ ipaddr_str(be_to_host32(b->your_ip)),
+ prefixlen);
+
+ if (sta->ipaddr == b->your_ip)
+ return;
+
+ if (sta->ipaddr != 0) {
+ wpa_printf(MSG_DEBUG,
+ "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+ ipaddr_str(be_to_host32(sta->ipaddr)));
+ hostapd_drv_br_delete_ip_neigh(hapd, 4,
+ (u8 *) &sta->ipaddr);
+ }
+
+ res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
+ prefixlen, sta->addr);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "dhcp_snoop: Adding ip neigh table failed: %d",
+ res);
+ return;
+ }
+ sta->ipaddr = b->your_ip;
+ }
+}
+
+
+int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+ hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
+ L2_PACKET_FILTER_DHCP);
+ if (hapd->sock_dhcp == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+ l2_packet_deinit(hapd->sock_dhcp);
+ hapd->sock_dhcp = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.h
new file mode 100644
index 0000000..93d0050
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.h
@@ -0,0 +1,30 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_SNOOP_H
+#define DHCP_SNOOP_H
+
+#ifdef CONFIG_PROXYARP
+
+int dhcp_snoop_init(struct hostapd_data *hapd);
+void dhcp_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* DHCP_SNOOP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.c
new file mode 100644
index 0000000..70dd18e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.c
@@ -0,0 +1,3976 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+#include "gas_serv.h"
+#include "wpa_auth.h"
+#include "beacon.h"
+#include "dpp_hostapd.h"
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+ struct dpp_authentication *auth);
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd);
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf);
+static int hostapd_dpp_process_conf_obj(void *ctx,
+ struct dpp_authentication *auth);
+#endif /* CONFIG_DPP2 */
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+
+/**
+ * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_bootstrap_info *bi;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd);
+ if (!bi)
+ return -1;
+
+ if (auth && auth->response_pending &&
+ dpp_notify_new_qr_code(auth, bi) == 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sending out pending authentication response");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+ auth->peer_mac_addr,
+ wpabuf_head(hapd->dpp_auth->resp_msg),
+ wpabuf_len(hapd->dpp_auth->resp_msg));
+ }
+
+#ifdef CONFIG_DPP2
+ dpp_controller_new_qr_code(hapd->iface->interfaces->dpp, bi);
+#endif /* CONFIG_DPP2 */
+
+ return bi->id;
+}
+
+
+/**
+ * hostapd_dpp_nfc_uri - Parse and add DPP bootstrapping info from NFC Tag (URI)
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a NFC Tag (URI NDEF message)
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, cmd);
+ if (!bi)
+ return -1;
+
+ return bi->id;
+}
+
+
+int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ struct dpp_bootstrap_info *peer_bi, *own_bi;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!own_bi)
+ return -1;
+
+ pos = os_strstr(cmd, " uri=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ peer_bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, pos);
+ if (!peer_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Failed to parse URI from NFC Handover Request");
+ return -1;
+ }
+
+ if (dpp_nfc_update_bi(own_bi, peer_bi) < 0)
+ return -1;
+
+ return peer_bi->id;
+}
+
+
+int hostapd_dpp_nfc_handover_sel(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ struct dpp_bootstrap_info *peer_bi, *own_bi;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!own_bi)
+ return -1;
+
+ pos = os_strstr(cmd, " uri=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ peer_bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, pos);
+ if (!peer_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Failed to parse URI from NFC Handover Select");
+ return -1;
+ }
+
+ if (peer_bi->curve != own_bi->curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Peer (NFC Handover Selector) used different curve");
+ return -1;
+ }
+
+ return peer_bi->id;
+}
+
+
+static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Retry Authentication Response after timeout");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg));
+}
+
+
+static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ unsigned int wait_time, max_tries;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ if (hapd->dpp_resp_max_tries)
+ max_tries = hapd->dpp_resp_max_tries;
+ else
+ max_tries = 5;
+ auth->auth_resp_tries++;
+ if (auth->auth_resp_tries >= max_tries) {
+ wpa_printf(MSG_INFO,
+ "DPP: No confirm received from initiator - stopping exchange");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (hapd->dpp_resp_retry_time)
+ wait_time = hapd->dpp_resp_retry_time;
+ else
+ wait_time = 1000;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Schedule retransmission of Authentication Response frame in %u ms",
+ wait_time);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+}
+
+
+static int hostapd_dpp_allow_ir(struct hostapd_data *hapd, unsigned int freq)
+{
+ int i, j;
+
+ if (!hapd->iface->hw_features)
+ return -1;
+
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode = &hapd->iface->hw_features[i];
+
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *chan = &mode->channels[j];
+
+ if (chan->freq != (int) freq)
+ continue;
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Frequency %u MHz not supported or does not allow PKEX initiation in the current channel list",
+ freq);
+
+ return 0;
+}
+
+
+static int hostapd_dpp_pkex_next_channel(struct hostapd_data *hapd,
+ struct dpp_pkex *pkex)
+{
+ if (pkex->freq == 2437)
+ pkex->freq = 5745;
+ else if (pkex->freq == 5745)
+ pkex->freq = 5220;
+ else if (pkex->freq == 5220)
+ pkex->freq = 60480;
+ else
+ return -1; /* no more channels to try */
+
+ if (hostapd_dpp_allow_ir(hapd, pkex->freq) == 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Try to initiate on %u MHz",
+ pkex->freq);
+ return 0;
+ }
+
+ /* Could not use this channel - try the next one */
+ return hostapd_dpp_pkex_next_channel(hapd, pkex);
+}
+
+
+static void hostapd_dpp_pkex_clear_code(struct hostapd_data *hapd)
+{
+ if (!hapd->dpp_pkex_code && !hapd->dpp_pkex_identifier)
+ return;
+
+ /* Delete PKEX code and identifier on successful completion of
+ * PKEX. We are not supposed to reuse these without being
+ * explicitly requested to perform PKEX again. */
+ wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+ os_free(hapd->dpp_pkex_code);
+ hapd->dpp_pkex_code = NULL;
+ os_free(hapd->dpp_pkex_identifier);
+ hapd->dpp_pkex_identifier = NULL;
+}
+
+
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_pkex_done(void *ctx, void *conn,
+ struct dpp_bootstrap_info *peer_bi)
+{
+ struct hostapd_data *hapd = ctx;
+ char cmd[500];
+ const char *pos;
+ u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ struct dpp_bootstrap_info *own_bi = NULL;
+ struct dpp_authentication *auth;
+
+ hostapd_dpp_pkex_clear_code(hapd);
+
+ os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+ hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+ wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
+ cmd);
+
+ pos = os_strstr(cmd, " own=");
+ if (pos) {
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
+ atoi(pos));
+ if (!own_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find bootstrapping info for the identified local entry");
+ return -1;
+ }
+
+ if (peer_bi->curve != own_bi->curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+ peer_bi->curve->name, own_bi->curve->name);
+ return -1;
+ }
+ }
+
+ pos = os_strstr(cmd, " role=");
+ if (pos) {
+ pos += 6;
+ if (os_strncmp(pos, "configurator", 12) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strncmp(pos, "enrollee", 8) == 0)
+ allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ else
+ return -1;
+ }
+
+ auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ peer_bi, own_bi, allowed_roles, 0,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ if (!auth)
+ return -1;
+
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, cmd) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ return dpp_tcp_auth(hapd->iface->interfaces->dpp, conn, auth,
+ hapd->conf->dpp_name, DPP_NETROLE_AP,
+ hapd->conf->dpp_mud_url,
+ hapd->conf->dpp_extra_conf_req_name,
+ hapd->conf->dpp_extra_conf_req_value,
+ hostapd_dpp_process_conf_obj, NULL);
+}
+#endif /* CONFIG_DPP2 */
+
+
+static int hostapd_dpp_pkex_init(struct hostapd_data *hapd,
+ enum dpp_pkex_ver ver,
+ const struct hostapd_ip_addr *ipaddr,
+ int tcp_port)
+{
+ struct dpp_pkex *pkex;
+ struct wpabuf *msg;
+ unsigned int wait_time;
+ bool v2 = ver != PKEX_VER_ONLY_1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Initiating PKEXv%d", v2 ? 2 : 1);
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+ hapd->dpp_pkex_identifier,
+ hapd->dpp_pkex_code, hapd->dpp_pkex_code_len, v2);
+ if (!pkex)
+ return -1;
+ pkex->forced_ver = ver != PKEX_VER_AUTO;
+
+ if (ipaddr) {
+#ifdef CONFIG_DPP2
+ return dpp_tcp_pkex_init(hapd->iface->interfaces->dpp, pkex,
+ ipaddr, tcp_port,
+ hapd->msg_ctx, hapd,
+ hostapd_dpp_pkex_done);
+#else /* CONFIG_DPP2 */
+ return -1;
+#endif /* CONFIG_DPP2 */
+ }
+
+ hapd->dpp_pkex = pkex;
+ msg = hapd->dpp_pkex->exchange_req;
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ pkex->freq = 2437;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(broadcast), pkex->freq,
+ v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
+ DPP_PA_PKEX_V1_EXCHANGE_REQ);
+ hostapd_drv_send_action(hapd, pkex->freq, 0, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg));
+ pkex->exch_req_wait_time = wait_time;
+ pkex->exch_req_tries = 1;
+
+ return 0;
+}
+
+
+static void hostapd_dpp_pkex_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_pkex *pkex = hapd->dpp_pkex;
+
+ if (!pkex || !pkex->exchange_req)
+ return;
+ if (pkex->exch_req_tries >= 5) {
+ if (hostapd_dpp_pkex_next_channel(hapd, pkex) < 0) {
+#ifdef CONFIG_DPP3
+ if (pkex->v2 && !pkex->forced_ver) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Fall back to PKEXv1");
+ hostapd_dpp_pkex_init(hapd, PKEX_VER_ONLY_1,
+ NULL, 0);
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No response from PKEX peer");
+ dpp_pkex_free(pkex);
+ hapd->dpp_pkex = NULL;
+ return;
+ }
+ pkex->exch_req_tries = 0;
+ }
+
+ pkex->exch_req_tries++;
+ wpa_printf(MSG_DEBUG, "DPP: Retransmit PKEX Exchange Request (try %u)",
+ pkex->exch_req_tries);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(broadcast), pkex->freq,
+ pkex->v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
+ DPP_PA_PKEX_V1_EXCHANGE_REQ);
+ hostapd_drv_send_action(hapd, pkex->freq, pkex->exch_req_wait_time,
+ broadcast,
+ wpabuf_head(pkex->exchange_req),
+ wpabuf_len(pkex->exchange_req));
+}
+
+
+static void hostapd_dpp_pkex_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t data_len, int ok)
+{
+ struct dpp_pkex *pkex = hapd->dpp_pkex;
+
+ if (pkex->failed) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate PKEX exchange due to an earlier error");
+ if (pkex->t > pkex->own_bi->pkex_t)
+ pkex->own_bi->pkex_t = pkex->t;
+ dpp_pkex_free(pkex);
+ hapd->dpp_pkex = NULL;
+ return;
+ }
+
+ if (pkex->exch_req_wait_time && pkex->exchange_req) {
+ /* Wait for PKEX Exchange Response frame and retry request if
+ * no response is seen. */
+ eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd,
+ NULL);
+ eloop_register_timeout(pkex->exch_req_wait_time / 1000,
+ (pkex->exch_req_wait_time % 1000) * 1000,
+ hostapd_dpp_pkex_retry_timeout, hapd,
+ NULL);
+ }
+}
+
+
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t data_len, int ok)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
+ MAC2STR(dst), ok);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+ " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
+
+ if (!hapd->dpp_auth) {
+ if (hapd->dpp_pkex) {
+ hostapd_dpp_pkex_tx_status(hapd, dst, data, data_len,
+ ok);
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore TX status since there is no ongoing authentication exchange");
+ return;
+ }
+
+#ifdef CONFIG_DPP2
+ if (auth->connect_on_tx_status) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Complete exchange on configuration result");
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+
+ if (hapd->dpp_auth->remove_on_tx_status) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate authentication exchange due to an earlier error");
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+ NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+#endif /* CONFIG_DPP2 */
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (hapd->dpp_auth_ok_on_ack)
+ hostapd_dpp_auth_success(hapd, 1);
+
+ if (!is_broadcast_ether_addr(dst) && !ok) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unicast DPP Action frame was not ACKed");
+ if (auth->waiting_auth_resp) {
+ /* In case of DPP Authentication Request frame, move to
+ * the next channel immediately. */
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_auth_init_next(hapd);
+ return;
+ }
+ if (auth->waiting_auth_conf) {
+ hostapd_dpp_auth_resp_retry(hapd);
+ return;
+ }
+ }
+
+ if (auth->waiting_auth_conf &&
+ auth->auth_resp_status == DPP_STATUS_OK) {
+ /* Make sure we do not get stuck waiting for Auth Confirm
+ * indefinitely after successfully transmitted Auth Response to
+ * allow new authentication exchanges to be started. */
+ eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd,
+ NULL);
+ eloop_register_timeout(1, 0, hostapd_dpp_auth_conf_wait_timeout,
+ hapd, NULL);
+ }
+
+ if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
+ /* Allow timeout handling to stop iteration if no response is
+ * received from a peer that has ACKed a request. */
+ auth->auth_req_ack = 1;
+ }
+
+ if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
+ hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+ hapd->dpp_auth->curr_freq,
+ hapd->dpp_auth->neg_freq);
+ hostapd_drv_send_action_cancel_wait(hapd);
+
+ if (hapd->dpp_auth->neg_freq !=
+ (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ hapd->dpp_auth->neg_freq, hapd->iface->freq);
+ }
+ }
+
+ if (hapd->dpp_auth_ok_on_ack)
+ hapd->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ unsigned int freq;
+ struct os_reltime now, diff;
+ unsigned int wait_time, diff_ms;
+
+ if (!auth || !auth->waiting_auth_resp)
+ return;
+
+ wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
+ diff_ms = diff.sec * 1000 + diff.usec / 1000;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
+ wait_time, diff_ms);
+
+ if (auth->auth_req_ack && diff_ms >= wait_time) {
+ /* Peer ACK'ed Authentication Request frame, but did not reply
+ * with Authentication Response frame within two seconds. */
+ wpa_printf(MSG_INFO,
+ "DPP: No response received from responder - stopping initiation attempt");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (diff_ms >= wait_time) {
+ /* Authentication Request frame was not ACK'ed and no reply
+ * was receiving within two seconds. */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue Initiator channel iteration");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ hostapd_dpp_auth_init_next(hapd);
+ return;
+ }
+
+ /* Driver did not support 2000 ms long wait_time with TX command, so
+ * schedule listen operation to continue waiting for the response.
+ *
+ * DPP listen operations continue until stopped, so simply schedule a
+ * new call to this function at the point when the two second reply
+ * wait has expired. */
+ wait_time -= diff_ms;
+
+ freq = auth->curr_freq;
+ if (auth->neg_freq > 0)
+ freq = auth->neg_freq;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue reply wait on channel %u MHz for %u ms",
+ freq, wait_time);
+ hapd->dpp_in_response_listen = 1;
+
+ if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ freq, hapd->iface->freq);
+ }
+
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reply_wait_timeout, hapd, NULL);
+}
+
+
+static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->waiting_auth_conf)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate authentication exchange due to Auth Confirm timeout");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No Auth Confirm received");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+ struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->dpp_config_obj_override)
+ auth->config_obj_override =
+ os_strdup(hapd->dpp_config_obj_override);
+ if (hapd->dpp_discovery_override)
+ auth->discovery_override =
+ os_strdup(hapd->dpp_discovery_override);
+ if (hapd->dpp_groups_override)
+ auth->groups_override = os_strdup(hapd->dpp_groups_override);
+ auth->ignore_netaccesskey_mismatch =
+ hapd->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ if (!hapd->dpp_auth)
+ return;
+ wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+ hostapd_dpp_auth_init_next(hapd);
+}
+
+
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ const u8 *dst;
+ unsigned int wait_time, max_wait_time, freq, max_tries, used;
+ struct os_reltime now, diff;
+
+ if (!auth)
+ return -1;
+
+ if (auth->freq_idx == 0)
+ os_get_reltime(&hapd->dpp_init_iter_start);
+
+ if (auth->freq_idx >= auth->num_freq) {
+ auth->num_freq_iters++;
+ if (hapd->dpp_init_max_tries)
+ max_tries = hapd->dpp_init_max_tries;
+ else
+ max_tries = 5;
+ if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
+ wpa_printf(MSG_INFO,
+ "DPP: No response received from responder - stopping initiation attempt");
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_INIT_FAILED);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return -1;
+ }
+ auth->freq_idx = 0;
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ if (hapd->dpp_init_retry_time)
+ wait_time = hapd->dpp_init_retry_time;
+ else
+ wait_time = 10000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
+ used = diff.sec * 1000 + diff.usec / 1000;
+ if (used > wait_time)
+ wait_time = 0;
+ else
+ wait_time -= used;
+ wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
+ wait_time);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ hostapd_dpp_init_timeout, hapd,
+ NULL);
+ return 0;
+ }
+ freq = auth->freq[auth->freq_idx++];
+ auth->curr_freq = freq;
+
+ if (!is_zero_ether_addr(auth->peer_mac_addr))
+ dst = auth->peer_mac_addr;
+ else if (is_zero_ether_addr(auth->peer_bi->mac_addr))
+ dst = broadcast;
+ else
+ dst = auth->peer_bi->mac_addr;
+ hapd->dpp_auth_ok_on_ack = 0;
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ max_wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ wait_time -= 10;
+ if (auth->neg_freq > 0 && freq != auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
+ freq, auth->neg_freq);
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
+ auth->auth_req_ack = 0;
+ os_get_reltime(&hapd->dpp_last_init);
+ return hostapd_drv_send_action(hapd, freq, wait_time,
+ dst,
+ wpabuf_head(hapd->dpp_auth->req_msg),
+ wpabuf_len(hapd->dpp_auth->req_msg));
+}
+
+
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_process_conf_obj(void *ctx,
+ struct dpp_authentication *auth)
+{
+ struct hostapd_data *hapd = ctx;
+ unsigned int i;
+
+ for (i = 0; i < auth->num_conf_obj; i++)
+ hostapd_dpp_handle_config_obj(hapd, auth,
+ &auth->conf_obj[i]);
+
+ return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+ struct dpp_authentication *auth;
+ u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ unsigned int neg_freq = 0;
+ int tcp = 0;
+#ifdef CONFIG_DPP2
+ int tcp_port = DPP_TCP_PORT;
+ struct hostapd_ip_addr ipaddr;
+ char *addr;
+#endif /* CONFIG_DPP2 */
+
+ pos = os_strstr(cmd, " peer=");
+ if (!pos)
+ return -1;
+ pos += 6;
+ peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!peer_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find bootstrapping info for the identified peer");
+ return -1;
+ }
+
+#ifdef CONFIG_DPP2
+ pos = os_strstr(cmd, " tcp_port=");
+ if (pos) {
+ pos += 10;
+ tcp_port = atoi(pos);
+ }
+
+ addr = get_param(cmd, " tcp_addr=");
+ if (addr && os_strcmp(addr, "from-uri") == 0) {
+ os_free(addr);
+ if (!peer_bi->host) {
+ wpa_printf(MSG_INFO,
+ "DPP: TCP address not available in peer URI");
+ return -1;
+ }
+ tcp = 1;
+ os_memcpy(&ipaddr, peer_bi->host, sizeof(ipaddr));
+ tcp_port = peer_bi->port;
+ } else if (addr) {
+ int res;
+
+ res = hostapd_parse_ip_addr(addr, &ipaddr);
+ os_free(addr);
+ if (res)
+ return -1;
+ tcp = 1;
+ }
+#endif /* CONFIG_DPP2 */
+
+ pos = os_strstr(cmd, " own=");
+ if (pos) {
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
+ atoi(pos));
+ if (!own_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find bootstrapping info for the identified local entry");
+ return -1;
+ }
+
+ if (peer_bi->curve != own_bi->curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+ peer_bi->curve->name, own_bi->curve->name);
+ return -1;
+ }
+ }
+
+ pos = os_strstr(cmd, " role=");
+ if (pos) {
+ pos += 6;
+ if (os_strncmp(pos, "configurator", 12) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strncmp(pos, "enrollee", 8) == 0)
+ allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ else
+ goto fail;
+ }
+
+ pos = os_strstr(cmd, " neg_freq=");
+ if (pos)
+ neg_freq = atoi(pos + 10);
+
+ if (!tcp && hapd->dpp_auth) {
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+ NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+#endif /* CONFIG_DPP2 */
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ }
+
+ auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ peer_bi, own_bi, allowed_roles, neg_freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ if (!auth)
+ goto fail;
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, cmd) < 0) {
+ dpp_auth_deinit(auth);
+ goto fail;
+ }
+
+ auth->neg_freq = neg_freq;
+
+ if (!is_zero_ether_addr(peer_bi->mac_addr))
+ os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN);
+
+#ifdef CONFIG_DPP2
+ if (tcp)
+ return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
+ &ipaddr, tcp_port, hapd->conf->dpp_name,
+ DPP_NETROLE_AP, hapd->conf->dpp_mud_url,
+ hapd->conf->dpp_extra_conf_req_name,
+ hapd->conf->dpp_extra_conf_req_value,
+ hapd->msg_ctx, hapd,
+ hostapd_dpp_process_conf_obj, NULL);
+#endif /* CONFIG_DPP2 */
+
+ hapd->dpp_auth = auth;
+ return hostapd_dpp_auth_init_next(hapd);
+fail:
+ return -1;
+}
+
+
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
+{
+ int freq;
+
+ freq = atoi(cmd);
+ if (freq <= 0)
+ return -1;
+
+ if (os_strstr(cmd, " role=configurator"))
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strstr(cmd, " role=enrollee"))
+ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ else
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+
+ if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ freq, hapd->iface->freq);
+ return -1;
+ }
+
+ hostapd_drv_dpp_listen(hapd, true);
+ return 0;
+}
+
+
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
+{
+ hostapd_drv_dpp_listen(hapd, false);
+ /* TODO: Stop listen operation on non-operating channel */
+}
+
+
+#ifdef CONFIG_DPP2
+static void
+hostapd_dpp_relay_needs_controller(struct hostapd_data *hapd, const u8 *src,
+ enum dpp_public_action_frame_type type)
+{
+ struct os_reltime now;
+
+ if (!hapd->conf->dpp_relay_port)
+ return;
+
+ os_get_reltime(&now);
+ if (hapd->dpp_relay_last_needs_ctrl.sec &&
+ !os_reltime_expired(&now, &hapd->dpp_relay_last_needs_ctrl, 60))
+ return;
+ hapd->dpp_relay_last_needs_ctrl = now;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RELAY_NEEDS_CONTROLLER
+ MACSTR " %u", MAC2STR(src), type);
+}
+#endif /* CONFIG_DPP2 */
+
+
+static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *r_bootstrap, *i_bootstrap;
+ u16 r_bootstrap_len, i_bootstrap_len;
+ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+ if (!hapd->iface->interfaces->dpp)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+ MAC2STR(src));
+
+#ifdef CONFIG_DPP2
+ hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+
+ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+
+ /* Try to find own and peer bootstrapping key matches based on the
+ * received hash values */
+ dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
+ r_bootstrap, &own_bi, &peer_bi);
+#ifdef CONFIG_DPP2
+ if (!own_bi) {
+ if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, i_bootstrap,
+ r_bootstrap, hapd) == 0)
+ return;
+ hostapd_dpp_relay_needs_controller(hapd, src,
+ DPP_PA_AUTHENTICATION_REQ);
+ }
+#endif /* CONFIG_DPP2 */
+ if (!own_bi) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No matching own bootstrapping key found - ignore message");
+ return;
+ }
+
+ if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No matching peer bootstrapping key found for PKEX - ignore message");
+ return;
+ }
+
+ if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching peer PKEX bootstrapping key - ignore message");
+ return;
+ }
+ }
+
+ if (hapd->dpp_auth) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Already in DPP authentication exchange - ignore new one");
+ return;
+ }
+
+ hapd->dpp_auth_ok_on_ack = 0;
+ hapd->dpp_auth = dpp_auth_req_rx(hapd->iface->interfaces->dpp,
+ hapd->msg_ctx, hapd->dpp_allowed_roles,
+ hapd->dpp_qr_mutual,
+ peer_bi, own_bi, freq, hdr, buf, len);
+ if (!hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG, "DPP: No response generated");
+ return;
+ }
+ hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+ if (dpp_set_configurator(hapd->dpp_auth,
+ hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+ os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), hapd->dpp_auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+ src, wpabuf_head(hapd->dpp_auth->resp_msg),
+ wpabuf_len(hapd->dpp_auth->resp_msg));
+}
+
+
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf)
+{
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
+ dpp_akm_str(conf->akm));
+ if (conf->ssid_len)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+ wpa_ssid_txt(conf->ssid, conf->ssid_len));
+ if (conf->connector) {
+ /* TODO: Save the Connector and consider using a command
+ * to fetch the value instead of sending an event with
+ * it. The Connector could end up being larger than what
+ * most clients are ready to receive as an event
+ * message. */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+ conf->connector);
+ }
+ if (conf->passphrase[0]) {
+ char hex[64 * 2 + 1];
+
+ wpa_snprintf_hex(hex, sizeof(hex),
+ (const u8 *) conf->passphrase,
+ os_strlen(conf->passphrase));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
+ hex);
+ } else if (conf->psk_set) {
+ char hex[PMK_LEN * 2 + 1];
+
+ wpa_snprintf_hex(hex, sizeof(hex), conf->psk, PMK_LEN);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
+ hex);
+ }
+ if (conf->c_sign_key) {
+ char *hex;
+ size_t hexlen;
+
+ hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
+ hex = os_malloc(hexlen);
+ if (hex) {
+ wpa_snprintf_hex(hex, hexlen,
+ wpabuf_head(conf->c_sign_key),
+ wpabuf_len(conf->c_sign_key));
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_C_SIGN_KEY "%s", hex);
+ os_free(hex);
+ }
+ }
+ if (auth->net_access_key) {
+ char *hex;
+ size_t hexlen;
+
+ hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+ hex = os_malloc(hexlen);
+ if (hex) {
+ wpa_snprintf_hex(hex, hexlen,
+ wpabuf_head(auth->net_access_key),
+ wpabuf_len(auth->net_access_key));
+ if (auth->net_access_key_expiry)
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+ (unsigned long)
+ auth->net_access_key_expiry);
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+ os_free(hex);
+ }
+ }
+}
+
+
+static int hostapd_dpp_handle_key_pkg(struct hostapd_data *hapd,
+ struct dpp_asymmetric_key *key)
+{
+#ifdef CONFIG_DPP2
+ int res;
+
+ if (!key)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Received Configurator backup");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+
+ while (key) {
+ res = dpp_configurator_from_backup(
+ hapd->iface->interfaces->dpp, key);
+ if (res < 0)
+ return -1;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFIGURATOR_ID "%d",
+ res);
+ key = key->next;
+ }
+#endif /* CONFIG_DPP2 */
+
+ return 0;
+}
+
+
+#ifdef CONFIG_DPP3
+static void hostapd_dpp_build_new_key(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->waiting_new_key)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key");
+ hostapd_dpp_start_gas_client(hapd);
+}
+#endif /* CONFIG_DPP3 */
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct hostapd_data *hapd = ctx;
+ const u8 *pos;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
+ int res;
+
+ if (!auth || !auth->auth_success) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return;
+ }
+ if (result != GAS_QUERY_AP_SUCCESS ||
+ !resp || status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+ goto fail;
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+ adv_proto);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+ resp);
+
+ if (wpabuf_len(adv_proto) != 10 ||
+ !(pos = wpabuf_head(adv_proto)) ||
+ pos[0] != WLAN_EID_ADV_PROTO ||
+ pos[1] != 8 ||
+ pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[4] != 5 ||
+ WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+ pos[8] != 0x1a ||
+ pos[9] != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not a DPP Advertisement Protocol ID");
+ goto fail;
+ }
+
+ res = dpp_conf_resp_rx(auth, resp);
+#ifdef CONFIG_DPP3
+ if (res == -3) {
+ wpa_printf(MSG_DEBUG, "DPP: New protocol key needed");
+ eloop_register_timeout(0, 0, hostapd_dpp_build_new_key, hapd,
+ NULL);
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+ goto fail;
+ }
+
+ hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
+ if (hostapd_dpp_handle_key_pkg(hapd, auth->conf_key_pkg) < 0)
+ goto fail;
+
+ status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_REJECT_CONFIG) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object");
+ status = DPP_STATUS_CONFIG_REJECTED;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+fail:
+ if (status != DPP_STATUS_OK)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+#ifdef CONFIG_DPP2
+ if (auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+ msg = dpp_build_conf_result(auth, status);
+ if (!msg)
+ goto fail2;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(addr), auth->curr_freq,
+ DPP_PA_CONFIGURATION_RESULT);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+ addr, wpabuf_head(msg),
+ wpabuf_len(msg));
+ wpabuf_free(msg);
+
+ /* This exchange will be terminated in the TX status handler */
+ auth->connect_on_tx_status = 1;
+ return;
+ }
+fail2:
+#endif /* CONFIG_DPP2 */
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *buf;
+ int res;
+
+ buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
+ DPP_NETROLE_AP,
+ hapd->conf->dpp_mud_url, NULL,
+ hapd->conf->dpp_extra_conf_req_name,
+ hapd->conf->dpp_extra_conf_req_value);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration request data available");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq);
+
+ res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
+ buf, hostapd_dpp_gas_resp_cb, hapd);
+ if (res < 0) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Failed to send Query Request");
+ wpabuf_free(buf);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: GAS query started with dialog token %u", res);
+ }
+}
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
+{
+ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+ dpp_notify_auth_success(hapd->dpp_auth, initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ if (hapd->dpp_auth->configurator) {
+ /* Prevent GAS response */
+ hapd->dpp_auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!hapd->dpp_auth->configurator)
+ hostapd_dpp_start_gas_client(hapd);
+}
+
+
+static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+ MAC2STR(src));
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - drop");
+ return;
+ }
+
+ if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+ os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+
+ if (auth->curr_freq != freq && auth->neg_freq == freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder accepted request for different negotiation channel");
+ auth->curr_freq = freq;
+ }
+
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+ if (!msg) {
+ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+ return;
+ }
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_CONF);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+ hapd->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+ MAC2STR(src));
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+ return;
+ }
+
+ hostapd_dpp_auth_success(hapd, 0);
+}
+
+
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->waiting_conf_result)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Timeout while waiting for Configuration Result");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->waiting_conf_result)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Timeout while waiting for Connection Status Result");
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_CONN_STATUS_RESULT "timeout");
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+#ifdef CONFIG_DPP3
+
+static bool hostapd_dpp_pb_active(struct hostapd_data *hapd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+ return ifaces && (ifaces->dpp_pb_time.sec ||
+ ifaces->dpp_pb_time.usec);
+}
+
+
+static void hostapd_dpp_remove_pb_hash(struct hostapd_data *hapd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ int i;
+
+ if (!ifaces->dpp_pb_bi)
+ return;
+ for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+ struct dpp_pb_info *info = &ifaces->dpp_pb[i];
+
+ if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+ continue;
+ if (os_memcmp(info->hash, ifaces->dpp_pb_resp_hash,
+ SHA256_MAC_LEN) == 0) {
+ /* Allow a new push button session to be established
+ * immediately without the successfully completed
+ * session triggering session overlap. */
+ info->rx_time.sec = 0;
+ info->rx_time.usec = 0;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+ }
+ }
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ enum dpp_status_error status;
+#ifdef CONFIG_DPP3
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
+ MAC2STR(src));
+
+ if (!auth || !auth->waiting_conf_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for result - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ status = dpp_conf_result_rx(auth, hdr, buf, len);
+
+ if (status == DPP_STATUS_OK && auth->send_conn_status) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+ auth->conf_resp_status);
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+ hapd, NULL);
+ auth->waiting_conn_status_result = 1;
+ eloop_cancel_timeout(
+ hostapd_dpp_conn_status_result_wait_timeout,
+ hapd, NULL);
+ eloop_register_timeout(
+ 16, 0, hostapd_dpp_conn_status_result_wait_timeout,
+ hapd, NULL);
+ return;
+ }
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ if (status == DPP_STATUS_OK)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+ "conf_status=%d", auth->conf_resp_status);
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+ NULL);
+#ifdef CONFIG_DPP3
+ if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+ if (status == DPP_STATUS_OK)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "success");
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "no-configuration-available");
+ ifaces->dpp_pb_result_indicated = true;
+ if (status == DPP_STATUS_OK)
+ hostapd_dpp_remove_pb_hash(hapd);
+ hostapd_dpp_push_button_stop(hapd);
+ }
+#endif /* CONFIG_DPP3 */
+}
+
+
+static void hostapd_dpp_rx_conn_status_result(struct hostapd_data *hapd,
+ const u8 *src, const u8 *hdr,
+ const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ enum dpp_status_error status;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len = 0;
+ char *channel_list = NULL;
+
+ wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+ if (!auth || !auth->waiting_conn_status_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for connection status result - drop");
+ return;
+ }
+
+ status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+ ssid, &ssid_len, &channel_list);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
+ "result=%d ssid=%s channel_list=%s",
+ status, wpa_ssid_txt(ssid, ssid_len),
+ channel_list ? channel_list : "N/A");
+ os_free(channel_list);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+ eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout,
+ hapd, NULL);
+}
+
+
+static void
+hostapd_dpp_rx_presence_announcement(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *r_bootstrap;
+ u16 r_bootstrap_len;
+ struct dpp_bootstrap_info *peer_bi;
+ struct dpp_authentication *auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Presence Announcement from " MACSTR,
+ MAC2STR(src));
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ peer_bi = dpp_bootstrap_find_chirp(hapd->iface->interfaces->dpp,
+ r_bootstrap);
+ dpp_notify_chirp_received(hapd->msg_ctx,
+ peer_bi ? (int) peer_bi->id : -1,
+ src, freq, r_bootstrap);
+ if (!peer_bi) {
+ if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, NULL,
+ r_bootstrap, hapd) == 0)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching bootstrapping information found");
+ hostapd_dpp_relay_needs_controller(
+ hapd, src, DPP_PA_PRESENCE_ANNOUNCEMENT);
+ return;
+ }
+
+ if (hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Presence Announcement during ongoing Authentication");
+ return;
+ }
+
+ auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ peer_bi, NULL, DPP_CAPAB_CONFIGURATOR, freq, NULL,
+ 0);
+ if (!auth)
+ return;
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth,
+ hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return;
+ }
+
+ auth->neg_freq = freq;
+
+ /* The source address of the Presence Announcement frame overrides any
+ * MAC address information from the bootstrapping information. */
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+ hapd->dpp_auth = auth;
+ if (hostapd_dpp_auth_init_next(hapd) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ }
+}
+
+
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+ u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
+ struct dpp_configurator *conf;
+ struct dpp_authentication *auth;
+ unsigned int wait_time, max_wait_time;
+ u16 group;
+
+ if (hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+ MAC2STR(src));
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(hapd->iface->interfaces->dpp,
+ csign_hash);
+ if (!conf) {
+ if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, NULL,
+ NULL, hapd) == 0)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ hostapd_dpp_relay_needs_controller(
+ hapd, src, DPP_PA_RECONFIG_ANNOUNCEMENT);
+ return;
+ }
+
+ fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &fcgroup_len);
+ if (!fcgroup || fcgroup_len != 2) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Finite Cyclic Group attribute");
+ return;
+ }
+ group = WPA_GET_LE16(fcgroup);
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+ a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+ e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+ auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ conf, freq, group, a_nonce, a_nonce_len,
+ e_id, e_id_len);
+ if (!auth)
+ return;
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return;
+ }
+
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ hapd->dpp_auth = auth;
+
+ hapd->dpp_in_response_listen = 0;
+ hapd->dpp_auth_ok_on_ack = 0;
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ max_wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+ wait_time -= 10;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+ if (hostapd_drv_send_action(hapd, freq, wait_time, src,
+ wpabuf_head(auth->reconfig_req_msg),
+ wpabuf_len(auth->reconfig_req_msg)) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ }
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *conf;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+ MACSTR, MAC2STR(src));
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return;
+
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+ if (hostapd_drv_send_action(hapd, freq, 500, src,
+ wpabuf_head(conf), wpabuf_len(conf)) < 0) {
+ wpabuf_free(conf);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+ wpabuf_free(conf);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
+ const u8 *src, unsigned int freq,
+ u8 trans_id,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ size_t len;
+
+ len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+#ifdef CONFIG_DPP2
+ len += 5;
+#endif /* CONFIG_DPP2 */
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, len);
+ if (!msg)
+ return;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
+ goto skip_trans_id;
+ }
+ if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
+ trans_id ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, trans_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_trans_id:
+ if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
+ goto skip_connector;
+ }
+ if (status == DPP_STATUS_OK &&
+ dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
+ char *connector;
+
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
+ connector = dpp_corrupt_connector_signature(
+ hapd->conf->dpp_connector);
+ if (!connector) {
+ wpabuf_free(msg);
+ return;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(connector));
+ wpabuf_put_str(msg, connector);
+ os_free(connector);
+ goto skip_connector;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Connector */
+ if (status == DPP_STATUS_OK) {
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+ wpabuf_put_str(msg, hapd->conf->dpp_connector);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_connector:
+ if (dpp_test == DPP_TEST_NO_PROTOCOL_VERSION_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Protocol Version");
+ goto skip_proto_ver;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1) {
+ u8 ver = DPP_VERSION;
+#ifdef CONFIG_DPP3
+ int conn_ver;
+
+ conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+ if (conn_ver > 0 && ver != conn_ver) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Use Connector version %d instead of current protocol version %d",
+ conn_ver, ver);
+ ver = conn_ver;
+ }
+#endif /* CONFIG_DPP3 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_PROTOCOL_VERSION_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Protocol Version");
+ ver = 1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, ver);
+ }
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_proto_ver:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
+ " status=%d", MAC2STR(src), status);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d status=%d", MAC2STR(src), freq,
+ DPP_PA_PEER_DISCOVERY_RESP, status);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
+static bool hapd_dpp_connector_available(struct hostapd_data *hapd)
+{
+ if (!hapd->wpa_auth ||
+ !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+ !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+ wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+ return false;
+ }
+
+ if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+ !hapd->conf->dpp_csign) {
+ wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+ return false;
+ }
+
+ return true;
+}
+
+
+static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
+ const u8 *src,
+ const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *connector, *trans_id;
+ u16 connector_len, trans_id_len;
+ struct os_time now;
+ struct dpp_introduction intro;
+ os_time_t expire;
+ int expiration;
+ enum dpp_status_error res;
+ u8 pkhash[SHA256_MAC_LEN];
+
+ os_memset(&intro, 0, sizeof(intro));
+
+ wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
+ MAC2STR(src));
+ if (!hapd_dpp_connector_available(hapd))
+ return;
+
+ os_get_time(&now);
+
+ if (hapd->conf->dpp_netaccesskey_expiry &&
+ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+ wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+ return;
+ }
+
+ trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ return;
+ }
+
+ connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+ if (!connector) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include its Connector");
+ return;
+ }
+
+ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey),
+ wpabuf_head(hapd->conf->dpp_csign),
+ wpabuf_len(hapd->conf->dpp_csign),
+ connector, connector_len, &expire, pkhash);
+ if (res == 255) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in internal failure (peer "
+ MACSTR ")", MAC2STR(src));
+ goto done;
+ }
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in failure (peer "
+ MACSTR " status %d)", MAC2STR(src), res);
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+ res);
+ goto done;
+ }
+
+#ifdef CONFIG_DPP3
+ if (intro.peer_version && intro.peer_version >= 2) {
+ const u8 *version;
+ u16 version_len;
+ u8 attr_version = 1;
+
+ version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && version_len >= 1)
+ attr_version = version[0];
+ if (attr_version != intro.peer_version) {
+ wpa_printf(MSG_INFO,
+ "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+ intro.peer_version, attr_version);
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq,
+ trans_id[0],
+ DPP_STATUS_NO_MATCH);
+ goto done;
+ }
+ }
+#endif /* CONFIG_DPP3 */
+
+ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+ expire = hapd->conf->dpp_netaccesskey_expiry;
+ if (expire)
+ expiration = expire - now.sec;
+ else
+ expiration = 0;
+
+ if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ intro.pmkid, expiration,
+ WPA_KEY_MGMT_DPP, pkhash) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+ goto done;
+ }
+
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+ DPP_STATUS_OK);
+done:
+ dpp_peer_intro_deinit(&intro);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq, bool v2)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+ MAC2STR(src));
+
+ if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_1 && v2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore PKEXv2 Exchange Request when configured to be PKEX v1 only");
+ return;
+ }
+ if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_2 && !v2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore PKEXv1 Exchange Request when configured to be PKEX v2 only");
+ return;
+ }
+
+ /* TODO: Support multiple PKEX codes by iterating over all the enabled
+ * values here */
+
+ if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code configured - ignore request");
+ goto try_relay;
+ }
+
+#ifdef CONFIG_DPP2
+ if (dpp_controller_is_own_pkex_req(hapd->iface->interfaces->dpp,
+ buf, len)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Exchange Request is from local Controller - ignore request");
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+
+ if (hapd->dpp_pkex) {
+ /* TODO: Support parallel operations */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Already in PKEX session - ignore new request");
+ goto try_relay;
+ }
+
+ hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
+ hapd->dpp_pkex_bi,
+ hapd->own_addr, src,
+ hapd->dpp_pkex_identifier,
+ hapd->dpp_pkex_code,
+ hapd->dpp_pkex_code_len,
+ buf, len, v2);
+ if (!hapd->dpp_pkex) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to process the request - ignore it");
+ goto try_relay;
+ }
+
+ msg = hapd->dpp_pkex->exchange_resp;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_EXCHANGE_RESP);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ if (hapd->dpp_pkex->failed) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate PKEX exchange due to an earlier error");
+ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+ hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ }
+
+ return;
+
+try_relay:
+#ifdef CONFIG_DPP2
+ if (v2 && dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, NULL, NULL,
+ hapd) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No Relay available for the message");
+ hostapd_dpp_relay_needs_controller(hapd, src,
+ DPP_PA_PKEX_EXCHANGE_REQ);
+ }
+#else /* CONFIG_DPP2 */
+ wpa_printf(MSG_DEBUG, "DPP: No relay functionality included - skip");
+#endif /* CONFIG_DPP2 */
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+ MAC2STR(src));
+
+ /* TODO: Support multiple PKEX codes by iterating over all the enabled
+ * values here */
+
+ if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
+ hapd->dpp_pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd, NULL);
+ hapd->dpp_pkex->exch_req_wait_time = 0;
+
+ msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
+ if (!msg) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+ MAC2STR(src));
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_COMMIT_REVEAL_REQ);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct wpabuf *msg;
+ struct dpp_pkex *pkex = hapd->dpp_pkex;
+ struct dpp_bootstrap_info *bi;
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+ MAC2STR(src));
+
+ if (!pkex || pkex->initiator || !pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+ if (!msg) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+ if (hapd->dpp_pkex->failed) {
+ wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
+ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+ hapd->dpp_pkex->own_bi->pkex_t =
+ hapd->dpp_pkex->t;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+ MACSTR, MAC2STR(src));
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_COMMIT_REVEAL_RESP);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+
+ hostapd_dpp_pkex_clear_code(hapd);
+ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
+ if (!bi)
+ return;
+ hapd->dpp_pkex = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ int res;
+ struct dpp_bootstrap_info *bi;
+ struct dpp_pkex *pkex = hapd->dpp_pkex;
+ char cmd[500];
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+ MAC2STR(src));
+
+ if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+ return;
+ }
+
+ hostapd_dpp_pkex_clear_code(hapd);
+ bi = dpp_pkex_finish(ifaces->dpp, pkex, src, freq);
+ if (!bi)
+ return;
+ hapd->dpp_pkex = NULL;
+
+#ifdef CONFIG_DPP3
+ if (ifaces->dpp_pb_bi &&
+ os_memcmp(bi->pubkey_hash_chirp, ifaces->dpp_pb_resp_hash,
+ SHA256_MAC_LEN) != 0) {
+ char id[20];
+
+ wpa_printf(MSG_INFO,
+ "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+ bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+ ifaces->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+ os_snprintf(id, sizeof(id), "%u", bi->id);
+ dpp_bootstrap_remove(ifaces->dpp, id);
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+
+ os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+ bi->id,
+ hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start authentication after PKEX with parameters: %s",
+ cmd);
+ if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication initialization failed");
+ return;
+ }
+}
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_pb_pkex_init(struct hostapd_data *hapd,
+ unsigned int freq, const u8 *src,
+ const u8 *r_hash)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ struct dpp_pkex *pkex;
+ struct wpabuf *msg;
+ char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL;
+ char cmd[300];
+ const char *password = NULL;
+ struct sae_password_entry *e;
+ int conf_id = -1;
+ bool sae = false, psk = false;
+ size_t len;
+
+ if (hapd->dpp_pkex) {
+ wpa_printf(MSG_DEBUG,
+ "PDP: Sending previously generated PKEX Exchange Request to "
+ MACSTR, MAC2STR(src));
+ msg = hapd->dpp_pkex->exchange_req;
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+ MACSTR, MAC2STR(src));
+
+ hapd->dpp_pkex_bi = ifaces->dpp_pb_bi;
+ os_memcpy(ifaces->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+ pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+ "PBPKEX", (const char *) ifaces->dpp_pb_c_nonce,
+ ifaces->dpp_pb_bi->curve->nonce_len,
+ true);
+ if (!pkex) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ pkex->freq = freq;
+
+ hapd->dpp_pkex = pkex;
+ msg = hapd->dpp_pkex->exchange_req;
+
+ if (ifaces->dpp_pb_cmd) {
+ /* Use the externally provided configuration */
+ os_free(hapd->dpp_pkex_auth_cmd);
+ len = 30 + os_strlen(ifaces->dpp_pb_cmd);
+ hapd->dpp_pkex_auth_cmd = os_malloc(len);
+ if (!hapd->dpp_pkex_auth_cmd) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+ hapd->dpp_pkex_bi->id, ifaces->dpp_pb_cmd);
+ goto send_frame;
+ }
+
+ /* Build config based on the current AP configuration */
+ wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+ (const u8 *) hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ /* TODO: If a local Configurator has been enabled, allow a
+ * DPP AKM credential to be provisioned by setting conf_id. */
+ }
+
+ if (hapd->conf->wpa & WPA_PROTO_RSN) {
+ psk = hapd->conf->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256);
+#ifdef CONFIG_SAE
+ sae = hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE;
+#endif /* CONFIG_SAE */
+ }
+
+#ifdef CONFIG_SAE
+ for (e = hapd->conf->sae_passwords; sae && e && !password;
+ e = e->next) {
+ if (e->identifier || !is_broadcast_ether_addr(e->peer_addr))
+ continue;
+ password = e->password;
+ }
+#endif /* CONFIG_SAE */
+ if (!password && hapd->conf->ssid.wpa_passphrase_set &&
+ hapd->conf->ssid.wpa_passphrase)
+ password = hapd->conf->ssid.wpa_passphrase;
+ if (password) {
+ len = 2 * os_strlen(password) + 1;
+ pass_hex = os_malloc(len);
+ if (!pass_hex) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ wpa_snprintf_hex(pass_hex, len, (const u8 *) password,
+ os_strlen(password));
+ }
+
+ if (conf_id > 0 && sae && psk && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-dpp+psk+sae configurator=%d ssid=%s pass=%s",
+ conf_id, ssid_hex, pass_hex);
+ } else if (conf_id > 0 && sae && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-dpp+sae configurator=%d ssid=%s pass=%s",
+ conf_id, ssid_hex, pass_hex);
+ } else if (conf_id > 0) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-dpp configurator=%d ssid=%s",
+ conf_id, ssid_hex);
+ } if (sae && psk && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-psk+sae ssid=%s pass=%s",
+ ssid_hex, pass_hex);
+ } else if (sae && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-sae ssid=%s pass=%s",
+ ssid_hex, pass_hex);
+ } else if (psk && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-psk ssid=%s pass=%s",
+ ssid_hex, pass_hex);
+ } else {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported AP configuration for push button");
+ str_clear_free(pass_hex);
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ str_clear_free(pass_hex);
+
+ os_free(hapd->dpp_pkex_auth_cmd);
+ len = 30 + os_strlen(cmd);
+ hapd->dpp_pkex_auth_cmd = os_malloc(len);
+ if (hapd->dpp_pkex_auth_cmd)
+ os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+ hapd->dpp_pkex_bi->id, cmd);
+ forced_memzero(cmd, sizeof(cmd));
+ if (!hapd->dpp_pkex_auth_cmd) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+
+send_frame:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_EXCHANGE_REQ);
+ hostapd_drv_send_action(hapd, pkex->freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ pkex->exch_req_wait_time = 2000;
+ pkex->exch_req_tries = 1;
+}
+
+
+static void
+hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd,
+ const u8 *src, const u8 *hdr,
+ const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ const u8 *r_hash;
+ u16 r_hash_len;
+ unsigned int i;
+ bool found = false;
+ struct dpp_pb_info *info, *tmp;
+ struct os_reltime now, age;
+ struct wpabuf *msg;
+
+ if (!ifaces)
+ return;
+
+ os_get_reltime(&now);
+ wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+ MACSTR, MAC2STR(src));
+
+ r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_hash_len);
+ if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_hash, r_hash_len);
+
+ for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+ info = &ifaces->dpp_pb[i];
+ if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+ os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Active push button Enrollee already known");
+ found = true;
+ info->rx_time = now;
+ }
+
+ if (!found) {
+ for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+ tmp = &ifaces->dpp_pb[i];
+ if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+ continue;
+
+ if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Push button Enrollee hash expired",
+ tmp->hash, SHA256_MAC_LEN);
+ tmp->rx_time.sec = 0;
+ tmp->rx_time.usec = 0;
+ continue;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Push button session overlap with hash",
+ tmp->hash, SHA256_MAC_LEN);
+ if (!ifaces->dpp_pb_result_indicated &&
+ hostapd_dpp_pb_active(hapd)) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_PB_RESULT "session-overlap");
+ ifaces->dpp_pb_result_indicated = true;
+ }
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+
+ /* Replace the oldest entry */
+ info = &ifaces->dpp_pb[0];
+ for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+ tmp = &ifaces->dpp_pb[i];
+ if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+ info = tmp;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+ os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+ info->rx_time = now;
+ }
+
+ if (!hostapd_dpp_pb_active(hapd)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Discard message since own push button has not been pressed");
+ return;
+ }
+
+ if (ifaces->dpp_pb_announce_time.sec == 0 &&
+ ifaces->dpp_pb_announce_time.usec == 0) {
+ /* Start a wait before allowing PKEX to be initiated */
+ ifaces->dpp_pb_announce_time = now;
+ }
+
+ if (!ifaces->dpp_pb_bi) {
+ int res;
+
+ res = dpp_bootstrap_gen(ifaces->dpp, "type=pkex");
+ if (res < 0)
+ return;
+ ifaces->dpp_pb_bi = dpp_bootstrap_get_id(ifaces->dpp, res);
+ if (!ifaces->dpp_pb_bi)
+ return;
+
+ if (random_get_bytes(ifaces->dpp_pb_c_nonce,
+ ifaces->dpp_pb_bi->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate C-nonce");
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ }
+
+ /* Skip the response if one was sent within last 50 ms since the
+ * Enrollee is going to send out at least three announcement messages.
+ */
+ os_reltime_sub(&now, &ifaces->dpp_pb_last_resp, &age);
+ if (age.sec == 0 && age.usec < 50000) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+ return;
+ }
+
+ msg = dpp_build_pb_announcement_resp(
+ ifaces->dpp_pb_bi, r_hash, ifaces->dpp_pb_c_nonce,
+ ifaces->dpp_pb_bi->curve->nonce_len);
+ if (!msg) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Send Push Button Presence Announcement Response to "
+ MACSTR, MAC2STR(src));
+ ifaces->dpp_pb_last_resp = now;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+
+ if (os_reltime_expired(&now, &ifaces->dpp_pb_announce_time, 15))
+ hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *trans_id, *version;
+ u16 trans_id_len, version_len;
+ struct wpabuf *msg;
+ u8 ver = DPP_VERSION;
+ int conn_ver;
+
+ wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from "
+ MACSTR, MAC2STR(src));
+
+ if (!hapd_dpp_connector_available(hapd))
+ return;
+
+ trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ return;
+ }
+
+ version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Protocol Version");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u",
+ trans_id[0], version[0]);
+
+ len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+ msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len);
+ if (!msg)
+ return;
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, trans_id[0]);
+
+ /* Protocol Version */
+ conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+ if (conn_ver > 0 && ver != conn_ver) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Use Connector version %d instead of current protocol version %d",
+ conn_ver, ver);
+ ver = conn_ver;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, ver);
+
+ /* DPP Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+ wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+ wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to "
+ MACSTR, MAC2STR(src));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PRIV_PEER_INTRO_NOTIFY);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct crypto_ec_key *own_key;
+ const struct dpp_curve_params *curve;
+ enum hpke_kem_id kem_id;
+ enum hpke_kdf_id kdf_id;
+ enum hpke_aead_id aead_id;
+ const u8 *aad = hdr;
+ size_t aad_len = DPP_HDR_LEN;
+ struct wpabuf *pt;
+ const u8 *trans_id, *wrapped, *version, *connector;
+ u16 trans_id_len, wrapped_len, version_len, connector_len;
+ struct os_time now;
+ struct dpp_introduction intro;
+ os_time_t expire;
+ int expiration;
+ enum dpp_status_error res;
+ u8 pkhash[SHA256_MAC_LEN];
+
+ os_memset(&intro, 0, sizeof(intro));
+
+ wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from "
+ MACSTR, MAC2STR(src));
+
+ if (!hapd_dpp_connector_available(hapd))
+ return;
+
+ os_get_time(&now);
+
+ if (hapd->conf->dpp_netaccesskey_expiry &&
+ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+ wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+ return;
+ }
+
+ trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ return;
+ }
+
+ wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_len);
+ if (!wrapped) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data");
+ return;
+ }
+
+ own_key = dpp_set_keypair(&curve,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey));
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ return;
+ }
+
+ if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d",
+ curve->ike_group);
+ crypto_ec_key_deinit(own_key);
+ return;
+ }
+
+ pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0,
+ aad, aad_len, wrapped, wrapped_len);
+ crypto_ec_key_deinit(own_key);
+ if (!pt) {
+ wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector");
+ return;
+ }
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt);
+
+ connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+ DPP_ATTR_CONNECTOR, &connector_len);
+ if (!connector) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include its Connector");
+ goto done;
+ }
+
+ version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+ DPP_ATTR_PROTOCOL_VERSION, &version_len);
+ if (!version || version_len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Protocol Version");
+ goto done;
+ }
+
+ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey),
+ wpabuf_head(hapd->conf->dpp_csign),
+ wpabuf_len(hapd->conf->dpp_csign),
+ connector, connector_len, &expire, pkhash);
+ if (res == 255) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in internal failure (peer "
+ MACSTR ")", MAC2STR(src));
+ goto done;
+ }
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in failure (peer "
+ MACSTR " status %d)", MAC2STR(src), res);
+ goto done;
+ }
+
+ if (intro.peer_version && intro.peer_version >= 2) {
+ u8 attr_version = 1;
+
+ if (version && version_len >= 1)
+ attr_version = version[0];
+ if (attr_version != intro.peer_version) {
+ wpa_printf(MSG_INFO,
+ "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+ intro.peer_version, attr_version);
+ goto done;
+ }
+ }
+
+ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+ expire = hapd->conf->dpp_netaccesskey_expiry;
+ if (expire)
+ expiration = expire - now.sec;
+ else
+ expiration = 0;
+
+ if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ intro.pmkid, expiration,
+ WPA_KEY_MGMT_DPP, pkhash) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+ goto done;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
+ MACSTR, MAC2STR(src));
+
+done:
+ dpp_peer_intro_deinit(&intro);
+ wpabuf_free(pt);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq)
+{
+ u8 crypto_suite;
+ enum dpp_public_action_frame_type type;
+ const u8 *hdr;
+ unsigned int pkex_t;
+
+ if (len < DPP_HDR_LEN)
+ return;
+ if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+ return;
+ hdr = buf;
+ buf += 4;
+ len -= 4;
+ crypto_suite = *buf++;
+ type = *buf++;
+ len -= 2;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+ MACSTR " freq=%u",
+ crypto_suite, type, MAC2STR(src), freq);
+ if (crypto_suite != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+ crypto_suite);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=unsupported-crypto-suite",
+ MAC2STR(src), freq, type);
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+ if (dpp_check_attrs(buf, len) < 0) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=invalid-attributes",
+ MAC2STR(src), freq, type);
+ return;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq, type);
+
+#ifdef CONFIG_DPP2
+ if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, NULL, NULL,
+ hapd) == 0)
+ return;
+#endif /* CONFIG_DPP2 */
+
+ switch (type) {
+ case DPP_PA_AUTHENTICATION_REQ:
+ hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_AUTHENTICATION_RESP:
+ hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_AUTHENTICATION_CONF:
+ hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
+ break;
+ case DPP_PA_PEER_DISCOVERY_REQ:
+ hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
+ break;
+#ifdef CONFIG_DPP3
+ case DPP_PA_PKEX_EXCHANGE_REQ:
+ /* This is for PKEXv2, but for now, process only with
+ * CONFIG_DPP3 to avoid issues with a capability that has not
+ * been tested with other implementations. */
+ hostapd_dpp_rx_pkex_exchange_req(hapd, src, hdr, buf, len, freq,
+ true);
+ break;
+#endif /* CONFIG_DPP3 */
+ case DPP_PA_PKEX_V1_EXCHANGE_REQ:
+ hostapd_dpp_rx_pkex_exchange_req(hapd, src, hdr, buf, len, freq,
+ false);
+ break;
+ case DPP_PA_PKEX_EXCHANGE_RESP:
+ hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
+ break;
+ case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+ hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+ hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
+ freq);
+ break;
+#ifdef CONFIG_DPP2
+ case DPP_PA_CONFIGURATION_RESULT:
+ hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
+ break;
+ case DPP_PA_CONNECTION_STATUS_RESULT:
+ hostapd_dpp_rx_conn_status_result(hapd, src, hdr, buf, len);
+ break;
+ case DPP_PA_PRESENCE_ANNOUNCEMENT:
+ hostapd_dpp_rx_presence_announcement(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
+ freq);
+ break;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+ case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+ hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr,
+ buf, len, freq);
+ break;
+ case DPP_PA_PRIV_PEER_INTRO_QUERY:
+ hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr,
+ buf, len, freq);
+ break;
+ case DPP_PA_PRIV_PEER_INTRO_UPDATE:
+ hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr,
+ buf, len, freq);
+ break;
+#endif /* CONFIG_DPP3 */
+ default:
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignored unsupported frame subtype %d", type);
+ break;
+ }
+
+ if (hapd->dpp_pkex)
+ pkex_t = hapd->dpp_pkex->t;
+ else if (hapd->dpp_pkex_bi)
+ pkex_t = hapd->dpp_pkex_bi->pkex_t;
+ else
+ pkex_t = 0;
+ if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
+ hostapd_dpp_pkex_remove(hapd, "*");
+ }
+}
+
+
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+ const u8 *query, size_t query_len,
+ const u8 *data, size_t data_len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+ if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
+ os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_DPP2
+ if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
+ data_len) == 0) {
+ /* Response will be forwarded once received over TCP */
+ return NULL;
+ }
+#endif /* CONFIG_DPP2 */
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return NULL;
+ }
+
+ if (hapd->dpp_auth_ok_on_ack && auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Have not received ACK for Auth Confirm yet - assume it was received based on this GAS request");
+ /* hostapd_dpp_auth_success() would normally have been called
+ * from TX status handler, but since there was no such handler
+ * call yet, simply send out the event message and proceed with
+ * exchange. */
+ dpp_notify_auth_success(hapd->dpp_auth, 1);
+ hapd->dpp_auth_ok_on_ack = 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Received Configuration Request (GAS Query Request)",
+ query, query_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
+ MAC2STR(sa));
+ resp = dpp_conf_req_rx(auth, query, query_len);
+ if (!resp)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ return resp;
+}
+
+
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+#ifdef CONFIG_DPP3
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
+
+ if (!auth)
+ return;
+
+#ifdef CONFIG_DPP3
+ if (auth->waiting_new_key && ok) {
+ wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key");
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+ ok);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+ if (ok && auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+ auth->waiting_conf_result = 1;
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+ hapd, NULL);
+ eloop_register_timeout(2, 0,
+ hostapd_dpp_config_result_wait_timeout,
+ hapd, NULL);
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+ hostapd_drv_send_action_cancel_wait(hapd);
+
+ if (ok)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+ "conf_status=%d", auth->conf_resp_status);
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+#ifdef CONFIG_DPP3
+ if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+ if (ok)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "success");
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "could-not-connect");
+ ifaces->dpp_pb_result_indicated = true;
+ if (ok)
+ hostapd_dpp_remove_pb_hash(hapd);
+ hostapd_dpp_push_button_stop(hapd);
+ }
+#endif /* CONFIG_DPP3 */
+}
+
+
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_authentication *auth;
+ int ret = -1;
+ char *curve = NULL;
+
+ auth = dpp_alloc_auth(hapd->iface->interfaces->dpp, hapd->msg_ctx);
+ if (!auth)
+ return -1;
+
+ curve = get_param(cmd, " curve=");
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, cmd) == 0 &&
+ dpp_configurator_own_config(auth, curve, 1) == 0) {
+ hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
+ ret = 0;
+ }
+
+ dpp_auth_deinit(auth);
+ os_free(curve);
+
+ return ret;
+}
+
+
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_bootstrap_info *own_bi;
+ const char *pos, *end;
+#ifdef CONFIG_DPP3
+ enum dpp_pkex_ver ver = PKEX_VER_AUTO;
+#else /* CONFIG_DPP3 */
+ enum dpp_pkex_ver ver = PKEX_VER_ONLY_1;
+#endif /* CONFIG_DPP3 */
+ int tcp_port = DPP_TCP_PORT;
+ struct hostapd_ip_addr *ipaddr = NULL;
+#ifdef CONFIG_DPP2
+ struct hostapd_ip_addr ipaddr_buf;
+ char *addr;
+
+ pos = os_strstr(cmd, " tcp_port=");
+ if (pos) {
+ pos += 10;
+ tcp_port = atoi(pos);
+ }
+
+ addr = get_param(cmd, " tcp_addr=");
+ if (addr) {
+ int res;
+
+ res = hostapd_parse_ip_addr(addr, &ipaddr_buf);
+ os_free(addr);
+ if (res)
+ return -1;
+ ipaddr = &ipaddr_buf;
+ }
+#endif /* CONFIG_DPP2 */
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not found");
+ return -1;
+ }
+ if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not for PKEX");
+ return -1;
+ }
+ hapd->dpp_pkex_bi = own_bi;
+ own_bi->pkex_t = 0; /* clear pending errors on new code */
+
+ os_free(hapd->dpp_pkex_identifier);
+ hapd->dpp_pkex_identifier = NULL;
+ pos = os_strstr(cmd, " identifier=");
+ if (pos) {
+ pos += 12;
+ end = os_strchr(pos, ' ');
+ if (!end)
+ return -1;
+ hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
+ if (!hapd->dpp_pkex_identifier)
+ return -1;
+ os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
+ hapd->dpp_pkex_identifier[end - pos] = '\0';
+ }
+
+ pos = os_strstr(cmd, " code=");
+ if (!pos)
+ return -1;
+ os_free(hapd->dpp_pkex_code);
+ hapd->dpp_pkex_code = os_strdup(pos + 6);
+ if (!hapd->dpp_pkex_code)
+ return -1;
+ hapd->dpp_pkex_code_len = os_strlen(hapd->dpp_pkex_code);
+
+ pos = os_strstr(cmd, " ver=");
+ if (pos) {
+ int v;
+
+ pos += 5;
+ v = atoi(pos);
+ if (v == 1)
+ ver = PKEX_VER_ONLY_1;
+ else if (v == 2)
+ ver = PKEX_VER_ONLY_2;
+ else
+ return -1;
+ }
+ hapd->dpp_pkex_ver = ver;
+
+ if (os_strstr(cmd, " init=1")) {
+ if (hostapd_dpp_pkex_init(hapd, ver, ipaddr, tcp_port) < 0)
+ return -1;
+ } else {
+#ifdef CONFIG_DPP2
+ dpp_controller_pkex_add(hapd->iface->interfaces->dpp, own_bi,
+ hapd->dpp_pkex_code,
+ hapd->dpp_pkex_identifier);
+#endif /* CONFIG_DPP2 */
+ }
+
+ /* TODO: Support multiple PKEX info entries */
+
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+ return 1;
+}
+
+
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ if ((id_val != 0 && id_val != 1))
+ return -1;
+
+ /* TODO: Support multiple PKEX entries */
+ os_free(hapd->dpp_pkex_code);
+ hapd->dpp_pkex_code = NULL;
+ os_free(hapd->dpp_pkex_identifier);
+ hapd->dpp_pkex_identifier = NULL;
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = NULL;
+ hapd->dpp_pkex_bi = NULL;
+ /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ return 0;
+}
+
+
+void hostapd_dpp_stop(struct hostapd_data *hapd)
+{
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+#ifdef CONFIG_DPP3
+ hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
+}
+
+
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_relay_tx(void *ctx, const u8 *addr, unsigned int freq,
+ const u8 *msg, size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ u8 *buf;
+
+ if (freq == 0)
+ freq = hapd->iface->freq;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
+ MAC2STR(addr), freq);
+ buf = os_malloc(2 + len);
+ if (!buf)
+ return;
+ buf[0] = WLAN_ACTION_PUBLIC;
+ buf[1] = WLAN_PA_VENDOR_SPECIFIC;
+ os_memcpy(buf + 2, msg, len);
+ hostapd_drv_send_action(hapd, freq, 0, addr, buf, 2 + len);
+ os_free(buf);
+}
+
+
+static void hostapd_dpp_relay_gas_resp_tx(void *ctx, const u8 *addr,
+ u8 dialog_token, int prot,
+ struct wpabuf *buf)
+{
+ struct hostapd_data *hapd = ctx;
+
+ gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf, 0);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static int hostapd_dpp_add_controllers(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_DPP2
+ struct dpp_controller_conf *ctrl;
+ struct dpp_relay_config config;
+
+ os_memset(&config, 0, sizeof(config));
+ config.msg_ctx = hapd->msg_ctx;
+ config.cb_ctx = hapd;
+ config.tx = hostapd_dpp_relay_tx;
+ config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+ for (ctrl = hapd->conf->dpp_controller; ctrl; ctrl = ctrl->next) {
+ config.ipaddr = &ctrl->ipaddr;
+ config.pkhash = ctrl->pkhash;
+ if (dpp_relay_add_controller(hapd->iface->interfaces->dpp,
+ &config) < 0)
+ return -1;
+ }
+
+ if (hapd->conf->dpp_relay_port)
+ dpp_relay_listen(hapd->iface->interfaces->dpp,
+ hapd->conf->dpp_relay_port,
+ &config);
+#endif /* CONFIG_DPP2 */
+
+ return 0;
+}
+
+
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_relay_config config;
+ struct hostapd_ip_addr addr;
+ u8 pkhash[SHA256_MAC_LEN];
+ char *pos, *tmp;
+ int ret = -1;
+ bool prev_state, new_state;
+ struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+ tmp = os_strdup(cmd);
+ if (!tmp)
+ goto fail;
+ pos = os_strchr(tmp, ' ');
+ if (!pos)
+ goto fail;
+ *pos++ = '\0';
+ if (hostapd_parse_ip_addr(tmp, &addr) < 0 ||
+ hexstr2bin(pos, pkhash, SHA256_MAC_LEN) < 0)
+ goto fail;
+
+ os_memset(&config, 0, sizeof(config));
+ config.msg_ctx = hapd->msg_ctx;
+ config.cb_ctx = hapd;
+ config.tx = hostapd_dpp_relay_tx;
+ config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+ config.ipaddr = &addr;
+ config.pkhash = pkhash;
+ prev_state = dpp_relay_controller_available(dpp);
+ ret = dpp_relay_add_controller(dpp, &config);
+ new_state = dpp_relay_controller_available(dpp);
+ if (new_state != prev_state)
+ ieee802_11_update_beacons(hapd->iface);
+fail:
+ os_free(tmp);
+ return ret;
+}
+
+
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd)
+{
+ struct hostapd_ip_addr addr;
+ bool prev_state, new_state;
+ struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+ if (hostapd_parse_ip_addr(cmd, &addr) < 0)
+ return;
+ prev_state = dpp_relay_controller_available(dpp);
+ dpp_relay_remove_controller(dpp, &addr);
+ new_state = dpp_relay_controller_available(dpp);
+ if (new_state != prev_state)
+ ieee802_11_update_beacons(hapd->iface);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+int hostapd_dpp_init(struct hostapd_data *hapd)
+{
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
+ hapd->dpp_init_done = 1;
+ return hostapd_dpp_add_controllers(hapd);
+}
+
+
+void hostapd_dpp_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(hapd->dpp_config_obj_override);
+ hapd->dpp_config_obj_override = NULL;
+ os_free(hapd->dpp_discovery_override);
+ hapd->dpp_discovery_override = NULL;
+ os_free(hapd->dpp_groups_override);
+ hapd->dpp_groups_override = NULL;
+ hapd->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!hapd->dpp_init_done)
+ return;
+ eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+ NULL);
+ eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
+ NULL);
+ hostapd_dpp_chirp_stop(hapd);
+ if (hapd->iface->interfaces) {
+ dpp_relay_stop_listen(hapd->iface->interfaces->dpp);
+ dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
+ }
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+ eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL);
+ hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ hostapd_dpp_pkex_remove(hapd, "*");
+ hapd->dpp_pkex = NULL;
+ os_free(hapd->dpp_configurator_params);
+ hapd->dpp_configurator_params = NULL;
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = NULL;
+}
+
+
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_controller_config config;
+ const char *pos;
+
+ os_memset(&config, 0, sizeof(config));
+ config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+ config.netrole = DPP_NETROLE_AP;
+ config.msg_ctx = hapd->msg_ctx;
+ config.cb_ctx = hapd;
+ config.process_conf_obj = hostapd_dpp_process_conf_obj;
+ if (cmd) {
+ pos = os_strstr(cmd, " tcp_port=");
+ if (pos) {
+ pos += 10;
+ config.tcp_port = atoi(pos);
+ }
+
+ pos = os_strstr(cmd, " role=");
+ if (pos) {
+ pos += 6;
+ if (os_strncmp(pos, "configurator", 12) == 0)
+ config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strncmp(pos, "enrollee", 8) == 0)
+ config.allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ else
+ return -1;
+ }
+
+ config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+ }
+ config.configurator_params = hapd->dpp_configurator_params;
+ return dpp_controller_start(hapd->iface->interfaces->dpp, &config);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
+
+static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: No chirp response received");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_chirp_next(hapd, NULL);
+}
+
+
+static void hostapd_dpp_chirp_start(struct hostapd_data *hapd)
+{
+ struct wpabuf *msg;
+ int type;
+
+ msg = hapd->dpp_presence_announcement;
+ type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+ wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", hapd->dpp_chirp_freq);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(broadcast), hapd->dpp_chirp_freq, type);
+ if (hostapd_drv_send_action(
+ hapd, hapd->dpp_chirp_freq, 2000, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg)) < 0 ||
+ eloop_register_timeout(2, 0, hostapd_dpp_chirp_timeout,
+ hapd, NULL) < 0)
+ hostapd_dpp_chirp_stop(hapd);
+}
+
+
+static struct hostapd_hw_modes *
+dpp_get_mode(struct hostapd_data *hapd,
+ enum hostapd_hw_mode mode)
+{
+ struct hostapd_hw_modes *modes = hapd->iface->hw_features;
+ u16 num_modes = hapd->iface->num_hw_features;
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode != mode ||
+ !modes[i].num_channels || !modes[i].channels)
+ continue;
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static void
+hostapd_dpp_chirp_scan_res_handler(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct wpa_scan_results *scan_res;
+ struct dpp_bootstrap_info *bi = hapd->dpp_chirp_bi;
+ unsigned int i;
+ struct hostapd_hw_modes *mode;
+ int c;
+ bool chan6 = hapd->iface->hw_features == NULL;
+
+ if (!bi)
+ return;
+
+ hapd->dpp_chirp_scan_done = 1;
+
+ scan_res = hostapd_driver_get_scan_results(hapd);
+
+ os_free(hapd->dpp_chirp_freqs);
+ hapd->dpp_chirp_freqs = NULL;
+
+ /* Channels from own bootstrapping info */
+ if (bi) {
+ for (i = 0; i < bi->num_freq; i++)
+ int_array_add_unique(&hapd->dpp_chirp_freqs,
+ bi->freq[i]);
+ }
+
+ /* Preferred chirping channels */
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211G);
+ if (mode) {
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR) ||
+ chan->freq != 2437)
+ continue;
+ chan6 = true;
+ break;
+ }
+ }
+ if (chan6)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
+
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211A);
+ if (mode) {
+ int chan44 = 0, chan149 = 0;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (chan->freq == 5220)
+ chan44 = 1;
+ if (chan->freq == 5745)
+ chan149 = 1;
+ }
+ if (chan149)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 5745);
+ else if (chan44)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 5220);
+ }
+
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211AD);
+ if (mode) {
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if ((chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR)) ||
+ chan->freq != 60480)
+ continue;
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 60480);
+ break;
+ }
+ }
+
+ /* Add channels from scan results for APs that advertise Configurator
+ * Connectivity element */
+ for (i = 0; scan_res && i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ size_t ie_len = bss->ie_len;
+
+ if (!ie_len)
+ ie_len = bss->beacon_ie_len;
+ if (get_vendor_ie((const u8 *) (bss + 1), ie_len,
+ DPP_CC_IE_VENDOR_TYPE))
+ int_array_add_unique(&hapd->dpp_chirp_freqs,
+ bss->freq);
+ }
+
+ if (!hapd->dpp_chirp_freqs ||
+ eloop_register_timeout(0, 0, hostapd_dpp_chirp_next,
+ hapd, NULL) < 0)
+ hostapd_dpp_chirp_stop(hapd);
+
+ wpa_scan_results_free(scan_res);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ int i;
+
+ if (hapd->dpp_chirp_listen)
+ hostapd_dpp_listen_stop(hapd);
+
+ if (hapd->dpp_chirp_freq == 0) {
+ if (hapd->dpp_chirp_round % 4 == 0 &&
+ !hapd->dpp_chirp_scan_done) {
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Update channel list for chirping");
+ os_memset(¶ms, 0, sizeof(params));
+ ret = hostapd_driver_scan(hapd, ¶ms);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to request a scan ret=%d (%s)",
+ ret, strerror(-ret));
+ hostapd_dpp_chirp_scan_res_handler(hapd->iface);
+ } else {
+ hapd->iface->scan_cb =
+ hostapd_dpp_chirp_scan_res_handler;
+ }
+ return;
+ }
+ hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[0];
+ hapd->dpp_chirp_round++;
+ wpa_printf(MSG_DEBUG, "DPP: Start chirping round %d",
+ hapd->dpp_chirp_round);
+ } else {
+ for (i = 0; hapd->dpp_chirp_freqs[i]; i++)
+ if (hapd->dpp_chirp_freqs[i] == hapd->dpp_chirp_freq)
+ break;
+ if (!hapd->dpp_chirp_freqs[i]) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Previous chirp freq %d not found",
+ hapd->dpp_chirp_freq);
+ return;
+ }
+ i++;
+ if (hapd->dpp_chirp_freqs[i]) {
+ hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[i];
+ } else {
+ hapd->dpp_chirp_iter--;
+ if (hapd->dpp_chirp_iter <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Chirping iterations completed");
+ hostapd_dpp_chirp_stop(hapd);
+ return;
+ }
+ hapd->dpp_chirp_freq = 0;
+ hapd->dpp_chirp_scan_done = 0;
+ if (eloop_register_timeout(30, 0,
+ hostapd_dpp_chirp_next,
+ hapd, NULL) < 0) {
+ hostapd_dpp_chirp_stop(hapd);
+ return;
+ }
+ if (hapd->dpp_chirp_listen) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Listen on %d MHz during chirp 30 second wait",
+ hapd->dpp_chirp_listen);
+ /* TODO: start listen on the channel */
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Wait 30 seconds before starting the next chirping round");
+ }
+ return;
+ }
+ }
+
+ hostapd_dpp_chirp_start(hapd);
+}
+
+
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ int iter = 1, listen_freq = 0;
+ struct dpp_bootstrap_info *bi;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not found");
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " iter=");
+ if (pos) {
+ iter = atoi(pos + 6);
+ if (iter <= 0)
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " listen=");
+ if (pos) {
+ listen_freq = atoi(pos + 8);
+ if (listen_freq <= 0)
+ return -1;
+ }
+
+ hostapd_dpp_chirp_stop(hapd);
+ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ hapd->dpp_qr_mutual = 0;
+ hapd->dpp_chirp_bi = bi;
+ hapd->dpp_presence_announcement = dpp_build_presence_announcement(bi);
+ if (!hapd->dpp_presence_announcement)
+ return -1;
+ hapd->dpp_chirp_iter = iter;
+ hapd->dpp_chirp_round = 0;
+ hapd->dpp_chirp_scan_done = 0;
+ hapd->dpp_chirp_listen = listen_freq;
+
+ return eloop_register_timeout(0, 0, hostapd_dpp_chirp_next, hapd, NULL);
+}
+
+
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd)
+{
+ if (hapd->dpp_presence_announcement) {
+ hostapd_drv_send_action_cancel_wait(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
+ }
+ hapd->dpp_chirp_bi = NULL;
+ wpabuf_free(hapd->dpp_presence_announcement);
+ hapd->dpp_presence_announcement = NULL;
+ if (hapd->dpp_chirp_listen)
+ hostapd_dpp_listen_stop(hapd);
+ hapd->dpp_chirp_listen = 0;
+ hapd->dpp_chirp_freq = 0;
+ os_free(hapd->dpp_chirp_freqs);
+ hapd->dpp_chirp_freqs = NULL;
+ eloop_cancel_timeout(hostapd_dpp_chirp_next, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_chirp_timeout, hapd, NULL);
+ if (hapd->iface->scan_cb == hostapd_dpp_chirp_scan_res_handler) {
+ /* TODO: abort ongoing scan */
+ hapd->iface->scan_cb = NULL;
+ }
+}
+
+
+static int handle_dpp_remove_bi(struct hostapd_iface *iface, void *ctx)
+{
+ struct dpp_bootstrap_info *bi = ctx;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (bi == hapd->dpp_chirp_bi)
+ hostapd_dpp_chirp_stop(hapd);
+ }
+
+ return 0;
+}
+
+
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi)
+{
+ struct hapd_interfaces *interfaces = ctx;
+
+ hostapd_for_each_interface(interfaces, handle_dpp_remove_bi, bi);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: Active push button mode expired");
+ hostapd_dpp_push_button_stop(hapd);
+}
+
+
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+ if (!ifaces || !ifaces->dpp)
+ return -1;
+ os_get_reltime(&ifaces->dpp_pb_time);
+ ifaces->dpp_pb_announce_time.sec = 0;
+ ifaces->dpp_pb_announce_time.usec = 0;
+ str_clear_free(ifaces->dpp_pb_cmd);
+ ifaces->dpp_pb_cmd = NULL;
+ if (cmd) {
+ ifaces->dpp_pb_cmd = os_strdup(cmd);
+ if (!ifaces->dpp_pb_cmd)
+ return -1;
+ }
+ eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
+ hapd, NULL);
+
+ return 0;
+}
+
+
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+ if (!ifaces || !ifaces->dpp)
+ return;
+ eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL);
+ if (hostapd_dpp_pb_active(hapd)) {
+ wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+ if (!ifaces->dpp_pb_result_indicated)
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_PB_RESULT "failed");
+ }
+ ifaces->dpp_pb_time.sec = 0;
+ ifaces->dpp_pb_time.usec = 0;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = NULL;
+
+ if (ifaces->dpp_pb_bi) {
+ char id[20];
+
+ os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
+ dpp_bootstrap_remove(ifaces->dpp, id);
+ ifaces->dpp_pb_bi = NULL;
+ }
+
+ ifaces->dpp_pb_result_indicated = false;
+
+ str_clear_free(ifaces->dpp_pb_cmd);
+ ifaces->dpp_pb_cmd = NULL;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+#ifdef CONFIG_DPP2
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd)
+{
+ return hapd->conf->dpp_configurator_connectivity ||
+ (hapd->iface->interfaces &&
+ dpp_relay_controller_available(hapd->iface->interfaces->dpp));
+}
+#endif /* CONFIG_DPP2 */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.h
new file mode 100644
index 0000000..55f1fce
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.h
@@ -0,0 +1,54 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_HOSTAPD_H
+#define DPP_HOSTAPD_H
+
+struct dpp_bootstrap_info;
+
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_handover_sel(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq);
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t data_len, int ok);
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+ const u8 *query, size_t query_len,
+ const u8 *data, size_t data_len);
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
+int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
+ char *buf, size_t buflen);
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+void hostapd_dpp_stop(struct hostapd_data *hapd);
+int hostapd_dpp_init(struct hostapd_data *hapd);
+void hostapd_dpp_deinit(struct hostapd_data *hapd);
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
+
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd);
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd);
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd);
+
+#endif /* DPP_HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/drv_callbacks.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/drv_callbacks.c
new file mode 100644
index 0000000..b0bba99
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/drv_callbacks.c
@@ -0,0 +1,2310 @@
+/*
+ * hostapd / Callback functions for driver wrappers
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/dpp.h"
+#include "common/sae.h"
+#include "common/hw_features_common.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "wnm_ap.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "tkip_countermeasures.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "hw_features.h"
+#include "dfs.h"
+#include "beacon.h"
+#include "mbo_ap.h"
+#include "dpp_hostapd.h"
+#include "fils_hlp.h"
+#include "neighbor_db.h"
+
+
+#ifdef CONFIG_FILS
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ u16 reply_res = WLAN_STATUS_SUCCESS;
+ struct ieee802_11_elems elems;
+ u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
+ int new_assoc;
+
+ wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
+ __func__, MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ if (!sta->fils_pending_assoc_req)
+ return;
+
+ ieee802_11_parse_elems(sta->fils_pending_assoc_req,
+ sta->fils_pending_assoc_req_len, &elems, 0);
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
+ __func__);
+ return;
+ }
+
+ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+ elems.fils_session,
+ sta->fils_hlp_resp);
+
+ reply_res = hostapd_sta_assoc(hapd, sta->addr,
+ sta->fils_pending_assoc_is_reassoc,
+ WLAN_STATUS_SUCCESS,
+ buf, p - buf);
+ ap_sta_set_authorized(hapd, sta, 1);
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+ hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+ fils_hlp_deinit(hapd);
+
+ /*
+ * Remove the station in case transmission of a success response fails
+ * (the STA was added associated to the driver) or if the station was
+ * previously added unassociated.
+ */
+ if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+}
+#endif /* CONFIG_FILS */
+
+
+static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if ((sta->flags &
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+ return false;
+
+ if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+
+ if (!sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA Query timeout
+ * has not been reached. Reject the association attempt
+ * temporarily and start SA Query, if one is not pending.
+ */
+ if (sta->sa_query_count == 0)
+ ap_sta_start_sa_query(hapd, sta);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *req_ies, size_t req_ies_len, int reassoc)
+{
+ struct sta_info *sta;
+ int new_assoc;
+ enum wpa_validate_result res;
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ielen;
+ u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+ u8 *p = buf;
+ u16 reason = WLAN_REASON_UNSPECIFIED;
+ int status = WLAN_STATUS_SUCCESS;
+ const u8 *p2p_dev_addr = NULL;
+ struct hostapd_ubus_request req = {
+ .type = HOSTAPD_UBUS_ASSOC_REQ,
+ .addr = addr,
+ };
+
+ if (addr == NULL) {
+ /*
+ * This could potentially happen with unexpected event from the
+ * driver wrapper. This was seen at least in one case where the
+ * driver ended up being set to station mode while hostapd was
+ * running, so better make sure we stop processing such an
+ * event here.
+ */
+ wpa_printf(MSG_DEBUG,
+ "hostapd_notif_assoc: Skip event with no address");
+ return -1;
+ }
+
+ if (is_multicast_ether_addr(addr) ||
+ is_zero_ether_addr(addr) ||
+ os_memcmp(addr, hapd->own_addr, ETH_ALEN) == 0) {
+ /* Do not process any frames with unexpected/invalid SA so that
+ * we do not add any state for unexpected STA addresses or end
+ * up sending out frames to unexpected destination. */
+ wpa_printf(MSG_DEBUG, "%s: Invalid SA=" MACSTR
+ " in received indication - ignore this indication silently",
+ __func__, MAC2STR(addr));
+ return 0;
+ }
+
+ random_add_randomness(addr, ETH_ALEN);
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "associated");
+
+ ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
+ if (elems.wps_ie) {
+ ie = elems.wps_ie - 2;
+ ielen = elems.wps_ie_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
+ } else if (elems.rsn_ie) {
+ ie = elems.rsn_ie - 2;
+ ielen = elems.rsn_ie_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
+ } else if (elems.wpa_ie) {
+ ie = elems.wpa_ie - 2;
+ ielen = elems.wpa_ie_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+#ifdef CONFIG_HS20
+ } else if (elems.osen) {
+ ie = elems.osen - 2;
+ ielen = elems.osen_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
+#endif /* CONFIG_HS20 */
+ } else {
+ ie = NULL;
+ ielen = 0;
+ wpa_printf(MSG_DEBUG,
+ "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ ap_sta_no_session_timeout(hapd, sta);
+ accounting_sta_stop(hapd, sta);
+
+ /*
+ * Make sure that the previously registered inactivity timer
+ * will not remove the STA immediately.
+ */
+ sta->timeout_next = STA_NULLFUNC;
+ } else {
+ sta = ap_sta_add(hapd, addr);
+ if (sta == NULL) {
+ hostapd_drv_sta_disassoc(hapd, addr,
+ WLAN_REASON_DISASSOC_AP_BUSY);
+ return -1;
+ }
+ }
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+ /*
+ * ACL configurations to the drivers (implementing AP SME and ACL
+ * offload) without hostapd's knowledge, can result in a disconnection
+ * though the driver accepts the connection. Skip the hostapd check for
+ * ACL if the driver supports ACL offload to avoid potentially
+ * conflicting ACL rules.
+ */
+ if (hapd->iface->drv_max_acl_mac_addrs == 0 &&
+ hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) {
+ wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
+ MAC2STR(addr));
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+
+ if (hostapd_ubus_handle_event(hapd, &req)) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
+ MAC2STR(req.addr));
+ goto fail;
+ }
+
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (sta->p2p_ie)
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef NEED_AP_MLME
+ if (elems.ht_capabilities &&
+ (hapd->iface->conf->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ struct ieee80211_ht_capabilities *ht_cap =
+ (struct ieee80211_ht_capabilities *)
+ elems.ht_capabilities;
+
+ if (le_to_host16(ht_cap->ht_capabilities_info) &
+ HT_CAP_INFO_40MHZ_INTOLERANT)
+ ht40_intolerant_add(hapd->iface, sta);
+ }
+#endif /* NEED_AP_MLME */
+
+ check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+
+#ifdef CONFIG_HS20
+ wpabuf_free(sta->hs20_ie);
+ if (elems.hs20 && elems.hs20_len > 4) {
+ sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+ elems.hs20_len - 4);
+ } else
+ sta->hs20_ie = NULL;
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems.roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems.roaming_cons_sel + 4,
+ elems.roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+ if (hapd->iface->fst)
+ sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+ else
+ sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
+ mbo_ap_check_sta_assoc(hapd, sta, &elems);
+
+ ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
+ elems.supp_op_classes_len);
+
+ if (hapd->conf->wpa) {
+ if (ie == NULL || ielen == 0) {
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state) {
+ wpa_printf(MSG_DEBUG,
+ "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ goto skip_wpa_check;
+ }
+#endif /* CONFIG_WPS */
+
+ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ goto fail;
+ }
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+ os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+ struct wpabuf *wps;
+
+ if (check_sa_query_need(hapd, sta)) {
+ status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+ p = hostapd_eid_assoc_comeback_time(hapd, sta,
+ p);
+
+ hostapd_sta_assoc(hapd, addr, reassoc, status,
+ buf, p - buf);
+ return 0;
+ }
+
+ sta->flags |= WLAN_STA_WPS;
+ wps = ieee802_11_vendor_ie_concat(ie, ielen,
+ WPS_IE_VENDOR_TYPE);
+ if (wps) {
+ if (wps_is_20(wps)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: STA supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
+ wpabuf_free(wps);
+ }
+ goto skip_wpa_check;
+ }
+#endif /* CONFIG_WPS */
+
+ if (check_sa_query_need(hapd, sta)) {
+ status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+ p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+ p - buf);
+ return 0;
+ }
+
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr,
+ p2p_dev_addr);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize WPA state machine");
+ return -1;
+ }
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
+ ie, ielen,
+ elems.rsnxe ? elems.rsnxe - 2 : NULL,
+ elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+ elems.mdie, elems.mdie_len,
+ elems.owe_dh, elems.owe_dh_len);
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ switch (res) {
+ case WPA_IE_OK:
+ reason = WLAN_REASON_UNSPECIFIED;
+ status = WLAN_STATUS_SUCCESS;
+ break;
+ case WPA_INVALID_IE:
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ break;
+ case WPA_INVALID_GROUP:
+ reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ break;
+ case WPA_INVALID_PAIRWISE:
+ reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ break;
+ case WPA_INVALID_AKMP:
+ reason = WLAN_REASON_AKMP_NOT_VALID;
+ status = WLAN_STATUS_AKMP_NOT_VALID;
+ break;
+ case WPA_NOT_ENABLED:
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ break;
+ case WPA_ALLOC_FAIL:
+ reason = WLAN_REASON_UNSPECIFIED;
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ break;
+ case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ break;
+ case WPA_INVALID_MGMT_GROUP_CIPHER:
+ reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
+ status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ break;
+ case WPA_INVALID_MDIE:
+ reason = WLAN_REASON_INVALID_MDE;
+ status = WLAN_STATUS_INVALID_MDIE;
+ break;
+ case WPA_INVALID_PROTO:
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ break;
+ case WPA_INVALID_PMKID:
+ reason = WLAN_REASON_INVALID_PMKID;
+ status = WLAN_STATUS_INVALID_PMKID;
+ break;
+ case WPA_DENIED_OTHER_REASON:
+ reason = WLAN_REASON_UNSPECIFIED;
+ status = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ break;
+ }
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "WPA/RSN information element rejected? (res %u)",
+ res);
+ wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
+ goto fail;
+ }
+
+ if (wpa_auth_uses_mfp(sta->wpa_sm))
+ sta->flags |= WLAN_STA_MFP;
+ else
+ sta->flags &= ~WLAN_STA_MFP;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+ req_ies_len);
+ if (status != WLAN_STATUS_SUCCESS) {
+ if (status == WLAN_STATUS_INVALID_PMKID)
+ reason = WLAN_REASON_INVALID_IE;
+ if (status == WLAN_STATUS_INVALID_MDIE)
+ reason = WLAN_REASON_INVALID_IE;
+ if (status == WLAN_STATUS_INVALID_FTIE)
+ reason = WLAN_REASON_INVALID_IE;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+ if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
+ sta->auth_alg == WLAN_AUTH_SAE &&
+ sta->sae && !sta->sae->h2e &&
+ ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_INFO, "SAE: " MACSTR
+ " indicates support for SAE H2E, but did not use it",
+ MAC2STR(sta->addr));
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+#endif /* CONFIG_SAE */
+ } else if (hapd->conf->wps_state) {
+#ifdef CONFIG_WPS
+ struct wpabuf *wps;
+
+ if (req_ies)
+ wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+ WPS_IE_VENDOR_TYPE);
+ else
+ wps = NULL;
+#ifdef CONFIG_WPS_STRICT
+ if (wps && wps_validate_assoc_req(wps) < 0) {
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ wpabuf_free(wps);
+ goto fail;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ if (wps) {
+ sta->flags |= WLAN_STA_WPS;
+ if (wps_is_20(wps)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: STA supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
+ } else
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ wpabuf_free(wps);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+ } else if (hapd->conf->osen) {
+ if (elems.osen == NULL) {
+ hostapd_logger(
+ hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No HS 2.0 OSEN element in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_WARNING,
+ "Failed to initialize WPA state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+ elems.osen - 2, elems.osen_len + 2) < 0)
+ return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
+ }
+#ifdef CONFIG_WPS
+skip_wpa_check:
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+ elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ wpa_printf(MSG_INFO,
+ "MBO: Reject WPA2 association without PMF");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_IEEE80211R_AP
+ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+ sta->auth_alg, req_ies, req_ies_len,
+ !elems.rsnxe);
+ if (!p) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ int delay_assoc = 0;
+
+ if (!req_ies)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
+ req_ies_len,
+ sta->fils_session)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Session validation failed");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
+ req_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Key Confirm validation failed");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Delaying Assoc Response (HLP)");
+ delay_assoc = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Going ahead with Assoc Response (no HLP)");
+ }
+
+ if (sta) {
+ wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ sta->fils_drv_assoc_finish = 0;
+ }
+
+ if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
+ u8 *req_tmp;
+
+ req_tmp = os_malloc(req_ies_len);
+ if (!req_tmp) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: buffer allocation failed for assoc req");
+ goto fail;
+ }
+ os_memcpy(req_tmp, req_ies, req_ies_len);
+ sta->fils_pending_assoc_req = req_tmp;
+ sta->fils_pending_assoc_req_len = req_ies_len;
+ sta->fils_pending_assoc_is_reassoc = reassoc;
+ sta->fils_drv_assoc_finish = 1;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ eloop_register_timeout(
+ 0, hapd->conf->fils_hlp_wait_time * 1024,
+ fils_hlp_timeout, hapd, sta);
+ return 0;
+ }
+ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+ elems.fils_session,
+ sta->fils_hlp_resp);
+ wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
+ buf, p - buf);
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems.owe_dh) {
+ u8 *npos;
+ u16 ret_status;
+
+ npos = owe_assoc_req_process(hapd, sta,
+ elems.owe_dh, elems.owe_dh_len,
+ p, sizeof(buf) - (p - buf),
+ &ret_status);
+ status = ret_status;
+ if (npos)
+ p = npos;
+
+ if (!npos &&
+ status == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+ hostapd_sta_assoc(hapd, addr, reassoc, ret_status, buf,
+ p - buf);
+ return 0;
+ }
+
+ if (!npos || status != WLAN_STATUS_SUCCESS)
+ goto fail;
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
+ elems.owe_dh) {
+ sta->dpp_pfs = dpp_pfs_init(
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey));
+ if (!sta->dpp_pfs) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not initialize PFS");
+ /* Try to continue without PFS */
+ goto pfs_fail;
+ }
+
+ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
+ elems.owe_dh_len) < 0) {
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+ }
+
+ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+ sta->dpp_pfs->secret : NULL);
+ pfs_fail:
+#endif /* CONFIG_DPP2 */
+
+ if (elems.rrm_enabled &&
+ elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
+ os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
+ sizeof(sta->rrm_enabled_capa));
+
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+ if (sta->auth_alg == WLAN_AUTH_FT ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)
+ ap_sta_set_authorized(hapd, sta, 1);
+#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+ /* Keep compiler silent about unused variables */
+ if (status) {
+ }
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+ hostapd_set_sta_flags(hapd, sta);
+
+ if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+#ifdef CONFIG_FILS
+ else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+#endif /* CONFIG_FILS */
+ else
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+ hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_P2P
+ if (req_ies) {
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+ req_ies, req_ies_len);
+ }
+#endif /* CONFIG_P2P */
+
+ return 0;
+
+fail:
+#ifdef CONFIG_IEEE80211R_AP
+ if (status >= 0)
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#endif /* CONFIG_IEEE80211R_AP */
+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+ ap_free_sta(hapd, sta);
+ return -1;
+}
+
+
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ if (addr == NULL) {
+ /*
+ * This could potentially happen with unexpected event from the
+ * driver wrapper. This was seen at least in one case where the
+ * driver ended up reporting a station mode event while hostapd
+ * was running, so better make sure we stop processing such an
+ * event here.
+ */
+ wpa_printf(MSG_DEBUG,
+ "hostapd_notif_disassoc: Skip event with no address");
+ return;
+ }
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated");
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "Disassociation notification for unknown STA "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
+ return;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "disconnected due to excessive missing ACKs");
+ hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+ ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+ enum smps_mode smps_mode,
+ enum chan_width chan_width, u8 rx_nss)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ const char *txt;
+
+ if (!sta)
+ return;
+
+ switch (smps_mode) {
+ case SMPS_AUTOMATIC:
+ txt = "automatic";
+ break;
+ case SMPS_OFF:
+ txt = "off";
+ break;
+ case SMPS_DYNAMIC:
+ txt = "dynamic";
+ break;
+ case SMPS_STATIC:
+ txt = "static";
+ break;
+ default:
+ txt = NULL;
+ break;
+ }
+ if (txt) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
+ MACSTR " %s", MAC2STR(addr), txt);
+ }
+
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ txt = "20(no-HT)";
+ break;
+ case CHAN_WIDTH_20:
+ txt = "20";
+ break;
+ case CHAN_WIDTH_40:
+ txt = "40";
+ break;
+ case CHAN_WIDTH_80:
+ txt = "80";
+ break;
+ case CHAN_WIDTH_80P80:
+ txt = "80+80";
+ break;
+ case CHAN_WIDTH_160:
+ txt = "160";
+ break;
+ case CHAN_WIDTH_320:
+ txt = "320";
+ break;
+ default:
+ txt = NULL;
+ break;
+ }
+ if (txt) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
+ MACSTR " %s", MAC2STR(addr), txt);
+ }
+
+ if (rx_nss != 0xff) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
+ MACSTR " %d", MAC2STR(addr), rx_nss);
+ }
+}
+
+
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset, int width, int cf1, int cf2,
+ u16 punct_bitmap, int finished)
+{
+#ifdef NEED_AP_MLME
+ int channel, chwidth, is_dfs0, is_dfs;
+ u8 seg0_idx = 0, seg1_idx = 0;
+ size_t i;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d, puncturing_bitmap=0x%x",
+ finished ? "had" : "starting",
+ hapd->iface->freq,
+ freq, ht, hapd->iconf->ch_switch_vht_config,
+ hapd->iconf->ch_switch_he_config,
+ hapd->iconf->ch_switch_eht_config, offset,
+ width, channel_width_to_string(width), cf1, cf2,
+ punct_bitmap);
+
+ if (!hapd->iface->current_mode) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "ignore channel switch since the interface is not yet ready");
+ return;
+ }
+
+ /* Check if any of configured channels require DFS */
+ is_dfs0 = hostapd_is_dfs_required(hapd->iface);
+ hapd->iface->freq = freq;
+
+ channel = hostapd_hw_get_channel(hapd, freq);
+ if (!channel) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver switched to bad channel!");
+ return;
+ }
+
+ switch (width) {
+ case CHAN_WIDTH_80:
+ chwidth = CONF_OPER_CHWIDTH_80MHZ;
+ break;
+ case CHAN_WIDTH_80P80:
+ chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+ break;
+ case CHAN_WIDTH_160:
+ chwidth = CONF_OPER_CHWIDTH_160MHZ;
+ break;
+ case CHAN_WIDTH_320:
+ chwidth = CONF_OPER_CHWIDTH_320MHZ;
+ break;
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ case CHAN_WIDTH_40:
+ default:
+ chwidth = CONF_OPER_CHWIDTH_USE_HT;
+ break;
+ }
+
+ switch (hapd->iface->current_mode->mode) {
+ case HOSTAPD_MODE_IEEE80211A:
+ if (cf1 == 5935)
+ seg0_idx = (cf1 - 5925) / 5;
+ else if (cf1 > 5950)
+ seg0_idx = (cf1 - 5950) / 5;
+ else if (cf1 > 5000)
+ seg0_idx = (cf1 - 5000) / 5;
+
+ if (cf2 == 5935)
+ seg1_idx = (cf2 - 5925) / 5;
+ else if (cf2 > 5950)
+ seg1_idx = (cf2 - 5950) / 5;
+ else if (cf2 > 5000)
+ seg1_idx = (cf2 - 5000) / 5;
+ break;
+ default:
+ ieee80211_freq_to_chan(cf1, &seg0_idx);
+ ieee80211_freq_to_chan(cf2, &seg1_idx);
+ break;
+ }
+
+ hapd->iconf->channel = channel;
+ hapd->iconf->ieee80211n = ht;
+ if (!ht) {
+ hapd->iconf->ieee80211ac = 0;
+ } else if (hapd->iconf->ch_switch_vht_config) {
+ /* CHAN_SWITCH VHT config */
+ if (hapd->iconf->ch_switch_vht_config &
+ CH_SWITCH_VHT_ENABLED)
+ hapd->iconf->ieee80211ac = 1;
+ else if (hapd->iconf->ch_switch_vht_config &
+ CH_SWITCH_VHT_DISABLED)
+ hapd->iconf->ieee80211ac = 0;
+ } else if (hapd->iconf->ch_switch_he_config) {
+ /* CHAN_SWITCH HE config */
+ if (hapd->iconf->ch_switch_he_config &
+ CH_SWITCH_HE_ENABLED)
+ hapd->iconf->ieee80211ax = 1;
+ else if (hapd->iconf->ch_switch_he_config &
+ CH_SWITCH_HE_DISABLED)
+ hapd->iconf->ieee80211ax = 0;
+#ifdef CONFIG_IEEE80211BE
+ } else if (hapd->iconf->ch_switch_eht_config) {
+ /* CHAN_SWITCH EHT config */
+ if (hapd->iconf->ch_switch_eht_config &
+ CH_SWITCH_EHT_ENABLED) {
+ hapd->iconf->ieee80211be = 1;
+ hapd->iconf->ieee80211ax = 1;
+ if (!is_6ghz_freq(hapd->iface->freq))
+ hapd->iconf->ieee80211ac = 1;
+ } else if (hapd->iconf->ch_switch_eht_config &
+ CH_SWITCH_EHT_DISABLED)
+ hapd->iconf->ieee80211be = 0;
+#endif /* CONFIG_IEEE80211BE */
+ }
+ hapd->iconf->ch_switch_vht_config = 0;
+ hapd->iconf->ch_switch_he_config = 0;
+ hapd->iconf->ch_switch_eht_config = 0;
+
+ if (width == CHAN_WIDTH_40 || width == CHAN_WIDTH_80 ||
+ width == CHAN_WIDTH_80P80 || width == CHAN_WIDTH_160 ||
+ width == CHAN_WIDTH_320)
+ hapd->iconf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ else if (width == CHAN_WIDTH_20 || width == CHAN_WIDTH_20_NOHT)
+ hapd->iconf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+
+ hapd->iconf->secondary_channel = offset;
+ hostapd_set_oper_chwidth(hapd->iconf, chwidth);
+ hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
+#ifdef CONFIG_IEEE80211BE
+ hapd->iconf->punct_bitmap = punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+ if (hapd->iconf->ieee80211ac) {
+ hapd->iconf->vht_capab &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (chwidth == CONF_OPER_CHWIDTH_160MHZ)
+ hapd->iconf->vht_capab |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ else if (chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
+ hapd->iconf->vht_capab |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ }
+
+ is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d puncturing_bitmap=0x%04x",
+ finished ? WPA_EVENT_CHANNEL_SWITCH :
+ WPA_EVENT_CHANNEL_SWITCH_STARTED,
+ freq, ht, offset, channel_width_to_string(width),
+ cf1, cf2, is_dfs0, is_dfs, punct_bitmap);
+ if (!finished)
+ return;
+
+ if (hapd->csa_in_progress &&
+ freq == hapd->cs_freq_params.freq) {
+ hostapd_cleanup_cs_params(hapd);
+ ieee802_11_set_beacon(hapd);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ "freq=%d dfs=%d", freq, is_dfs);
+ } else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+ /* Complete AP configuration for the first bring up. */
+ if (is_dfs0 > 0 &&
+ hostapd_is_dfs_required(hapd->iface) <= 0 &&
+ hapd->iface->state != HAPD_IFACE_ENABLED) {
+ /* Fake a CAC start bit to skip setting channel */
+ hapd->iface->cac_started = 1;
+ hostapd_setup_interface_complete(hapd->iface, 0);
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ "freq=%d dfs=%d", freq, is_dfs);
+ } else if (is_dfs &&
+ hostapd_is_dfs_required(hapd->iface) &&
+ !hostapd_is_dfs_chan_available(hapd->iface) &&
+ !hapd->iface->cac_started) {
+ hostapd_disable_iface(hapd->iface);
+ hostapd_enable_iface(hapd->iface);
+ }
+
+ for (i = 0; i < hapd->iface->num_bss; i++)
+ hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
+
+#ifdef CONFIG_OCV
+ if (hapd->conf->ocv &&
+ !(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP)) {
+ struct sta_info *sta;
+ bool check_sa_query = false;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+ !(sta->flags & WLAN_STA_WNM_SLEEP_MODE)) {
+ sta->post_csa_sa_query = 1;
+ check_sa_query = true;
+ }
+ }
+
+ if (check_sa_query) {
+ wpa_printf(MSG_DEBUG,
+ "OCV: Check post-CSA SA Query initiation in 15 seconds");
+ eloop_register_timeout(15, 0,
+ hostapd_ocv_check_csa_sa_query,
+ hapd, NULL);
+ }
+ }
+#endif /* CONFIG_OCV */
+#endif /* NEED_AP_MLME */
+}
+
+
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code)
+{
+ switch (reason_code) {
+ case MAX_CLIENT_REACHED:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
+ MAC2STR(addr));
+ break;
+ case BLOCKED_CLIENT:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
+ MAC2STR(addr));
+ break;
+ }
+}
+
+
+#ifdef CONFIG_ACS
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+ struct acs_selected_channels *acs_res)
+{
+ int ret, i;
+ int err = 0;
+ struct hostapd_channel_data *pri_chan;
+
+ if (hapd->iconf->channel) {
+ wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+ hapd->iconf->channel);
+ return;
+ }
+
+ hapd->iface->freq = acs_res->pri_freq;
+
+ if (!hapd->iface->current_mode) {
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode =
+ &hapd->iface->hw_features[i];
+
+ if (mode->mode == acs_res->hw_mode) {
+ if (hapd->iface->freq > 0 &&
+ !hw_get_chan(mode->mode,
+ hapd->iface->freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features))
+ continue;
+ hapd->iface->current_mode = mode;
+ break;
+ }
+ }
+ if (!hapd->iface->current_mode) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver selected to bad hw_mode");
+ err = 1;
+ goto out;
+ }
+ }
+
+ if (!acs_res->pri_freq) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver switched to bad channel");
+ err = 1;
+ goto out;
+ }
+ pri_chan = hw_get_channel_freq(hapd->iface->current_mode->mode,
+ acs_res->pri_freq, NULL,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ if (!pri_chan) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Could not determine primary channel number from pri_freq %u",
+ acs_res->pri_freq);
+ err = 1;
+ goto out;
+ }
+
+ hapd->iconf->channel = pri_chan->chan;
+ hapd->iconf->acs = 1;
+
+ if (acs_res->sec_freq == 0)
+ hapd->iconf->secondary_channel = 0;
+ else if (acs_res->sec_freq < acs_res->pri_freq)
+ hapd->iconf->secondary_channel = -1;
+ else if (acs_res->sec_freq > acs_res->pri_freq)
+ hapd->iconf->secondary_channel = 1;
+ else {
+ wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+ err = 1;
+ goto out;
+ }
+
+ hapd->iconf->edmg_channel = acs_res->edmg_channel;
+
+ if (hapd->iface->conf->ieee80211ac || hapd->iface->conf->ieee80211ax) {
+ /* set defaults for backwards compatibility */
+ hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+ hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0);
+ hostapd_set_oper_chwidth(hapd->iconf, CONF_OPER_CHWIDTH_USE_HT);
+ if (acs_res->ch_width == 40) {
+ if (is_6ghz_freq(acs_res->pri_freq))
+ hostapd_set_oper_centr_freq_seg0_idx(
+ hapd->iconf,
+ acs_res->vht_seg0_center_ch);
+ } else if (acs_res->ch_width == 80) {
+ hostapd_set_oper_centr_freq_seg0_idx(
+ hapd->iconf, acs_res->vht_seg0_center_ch);
+ if (acs_res->vht_seg1_center_ch == 0) {
+ hostapd_set_oper_chwidth(
+ hapd->iconf, CONF_OPER_CHWIDTH_80MHZ);
+ } else {
+ hostapd_set_oper_chwidth(
+ hapd->iconf,
+ CONF_OPER_CHWIDTH_80P80MHZ);
+ hostapd_set_oper_centr_freq_seg1_idx(
+ hapd->iconf,
+ acs_res->vht_seg1_center_ch);
+ }
+ } else if (acs_res->ch_width == 160) {
+ hostapd_set_oper_chwidth(hapd->iconf,
+ CONF_OPER_CHWIDTH_160MHZ);
+ hostapd_set_oper_centr_freq_seg0_idx(
+ hapd->iconf, acs_res->vht_seg1_center_ch);
+ }
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iface->conf->ieee80211be && acs_res->ch_width == 320) {
+ hostapd_set_oper_chwidth(hapd->iconf, CONF_OPER_CHWIDTH_320MHZ);
+ hostapd_set_oper_centr_freq_seg0_idx(
+ hapd->iconf, acs_res->vht_seg1_center_ch);
+ hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+ }
+
+ if (hapd->iface->conf->ieee80211be && acs_res->puncture_bitmap)
+ hapd->iconf->punct_bitmap = acs_res->puncture_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
+out:
+ ret = hostapd_acs_completed(hapd->iface, err);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Possibly channel configuration is invalid");
+ }
+}
+#endif /* CONFIG_ACS */
+
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ int ssi_signal)
+{
+ size_t i;
+ int ret = 0;
+
+ if (sa == NULL || ie == NULL)
+ return -1;
+
+ random_add_randomness(sa, ETH_ALEN);
+ for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+ if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+ sa, da, bssid, ie, ie_len,
+ ssi_signal) > 0) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+#ifdef HOSTAPD
+
+#ifdef CONFIG_IEEE80211R_AP
+static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+ const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL)
+ return;
+
+ hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ sta->flags |= WLAN_STA_AUTH;
+
+ hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifdef CONFIG_FILS
+static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub)
+{
+ if (resp == WLAN_STATUS_SUCCESS) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_FILS_SK;
+ mlme_authenticate_indication(hapd, sta);
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication failed (FILS)");
+ }
+
+ hostapd_sta_auth(hapd, sta->addr, 2, resp,
+ data ? wpabuf_head(data) : NULL,
+ data ? wpabuf_len(data) : 0);
+ wpabuf_free(data);
+}
+#endif /* CONFIG_FILS */
+
+
+static void hostapd_notif_auth(struct hostapd_data *hapd,
+ struct auth_info *rx_auth)
+{
+ struct sta_info *sta;
+ u16 status = WLAN_STATUS_SUCCESS;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+ size_t resp_ies_len = 0;
+
+ sta = ap_get_sta(hapd, rx_auth->peer);
+ if (!sta) {
+ sta = ap_sta_add(hapd, rx_auth->peer);
+ if (sta == NULL) {
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+ }
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+#ifdef CONFIG_IEEE80211R_AP
+ if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+ sta->auth_alg = WLAN_AUTH_FT;
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to initialize WPA state machine");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+ rx_auth->auth_transaction, rx_auth->ies,
+ rx_auth->ies_len,
+ hostapd_notify_auth_ft_finish, hapd);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
+ sta->auth_alg = WLAN_AUTH_FILS_SK;
+ handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
+ rx_auth->auth_type, rx_auth->auth_transaction,
+ rx_auth->status_code,
+ hostapd_notify_auth_fils_finish);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+fail:
+ hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+ status, resp_ies, resp_ies_len);
+}
+
+
+#ifndef NEED_AP_MLME
+static void hostapd_action_rx(struct hostapd_data *hapd,
+ struct rx_mgmt *drv_mgmt)
+{
+ struct ieee80211_mgmt *mgmt;
+ struct sta_info *sta;
+ size_t plen __maybe_unused;
+ u16 fc;
+ u8 *action __maybe_unused;
+
+ if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
+ return;
+
+ plen = drv_mgmt->frame_len - IEEE80211_HDRLEN;
+
+ mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
+ fc = le_to_host16(mgmt->frame_control);
+ if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
+ return; /* handled by the driver */
+
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " plen %d",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+ return;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (mgmt->u.action.category == WLAN_ACTION_FT) {
+ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
+ ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
+ return;
+ }
+#ifdef CONFIG_WNM_AP
+ if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
+ return;
+ }
+#endif /* CONFIG_WNM_AP */
+#ifdef CONFIG_FST
+ if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
+ fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
+ return;
+ }
+#endif /* CONFIG_FST */
+#ifdef CONFIG_DPP
+ if (plen >= 2 + 4 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.oui;
+ end = drv_mgmt->frame + drv_mgmt->frame_len;
+ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+ drv_mgmt->freq);
+ return;
+ }
+#endif /* CONFIG_DPP */
+}
+#endif /* NEED_AP_MLME */
+
+
+#ifdef NEED_AP_MLME
+
+static struct hostapd_data *
+switch_link_hapd(struct hostapd_data *hapd, int link_id)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && link_id >= 0) {
+ struct hostapd_data *link_bss;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+ if (link_bss)
+ return link_bss;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd;
+}
+
+
+#define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ const u8 *bssid)
+{
+ size_t i;
+
+ if (bssid == NULL)
+ return NULL;
+ if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
+ bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
+ return HAPD_BROADCAST;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+ return iface->bss[i];
+ }
+
+ return NULL;
+}
+
+
+static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+ const u8 *bssid, const u8 *addr,
+ int wds)
+{
+ hapd = get_hapd_bssid(hapd->iface, bssid);
+ if (hapd == NULL || hapd == HAPD_BROADCAST)
+ return;
+
+ ieee802_11_rx_from_unknown(hapd, addr, wds);
+}
+
+
+static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+{
+ struct hostapd_iface *iface;
+ const struct ieee80211_hdr *hdr;
+ const u8 *bssid;
+ struct hostapd_frame_info fi;
+ int ret;
+ bool is_mld = false;
+
+ hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
+ iface = hapd->iface;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_mgmt_frame_handling) {
+ size_t hex_len = 2 * rx_mgmt->frame_len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
+ rx_mgmt->frame_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
+ os_free(hex);
+ }
+ return 1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
+ bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
+ if (bssid == NULL)
+ return 0;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, bssid, ETH_ALEN) == 0)
+ is_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!is_mld)
+ hapd = get_hapd_bssid(iface, bssid);
+
+ if (!hapd) {
+ u16 fc = le_to_host16(hdr->frame_control);
+
+ /*
+ * Drop frames to unknown BSSIDs except for Beacon frames which
+ * could be used to update neighbor information.
+ */
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ hapd = iface->bss[0];
+ else
+ return 0;
+ }
+
+ os_memset(&fi, 0, sizeof(fi));
+ fi.freq = rx_mgmt->freq;
+ fi.datarate = rx_mgmt->datarate;
+ fi.ssi_signal = rx_mgmt->ssi_signal;
+
+ if (hapd == HAPD_BROADCAST) {
+ size_t i;
+
+ ret = 0;
+ for (i = 0; i < iface->num_bss; i++) {
+ /* if bss is set, driver will call this function for
+ * each bss individually. */
+ if (rx_mgmt->drv_priv &&
+ (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
+ continue;
+
+ if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+ rx_mgmt->frame_len, &fi) > 0)
+ ret = 1;
+ }
+ } else
+ ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+ &fi);
+
+ random_add_randomness(&fi, sizeof(fi));
+
+ return ret;
+}
+
+
+static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+ size_t len, u16 stype, int ok, int link_id)
+{
+ struct ieee80211_hdr *hdr;
+ struct hostapd_data *orig_hapd, *tmp_hapd;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && link_id != -1) {
+ tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (tmp_hapd)
+ hapd = tmp_hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ orig_hapd = hapd;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ if (tmp_hapd) {
+ hapd = tmp_hapd;
+#ifdef CONFIG_IEEE80211BE
+ } else if (hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, get_hdr_bssid(hdr, len),
+ ETH_ALEN) == 0) {
+ /* AP MLD address match - use hapd pointer as-is */
+#endif /* CONFIG_IEEE80211BE */
+ } else {
+ return;
+ }
+
+ if (hapd == HAPD_BROADCAST) {
+ if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
+ buf[24] != WLAN_ACTION_PUBLIC)
+ return;
+ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
+ if (!hapd || hapd == HAPD_BROADCAST)
+ return;
+ /*
+ * Allow processing of TX status for a Public Action frame that
+ * used wildcard BBSID.
+ */
+ }
+ ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (sta)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
+ " - adding a new STA", MAC2STR(addr));
+ sta = ap_sta_add(hapd, addr);
+ if (sta) {
+ hostapd_new_assoc_sta(hapd, sta, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
+ MAC2STR(addr));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
+ const u8 *src)
+{
+ struct sta_info *sta;
+ unsigned int j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ sta = ap_get_sta(iface->bss[j], src);
+ if (sta && sta->flags & WLAN_STA_ASSOC)
+ return iface->bss[j];
+ }
+
+ return NULL;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ const u8 *data, size_t data_len,
+ enum frame_encryption encrypted,
+ int link_id)
+{
+ struct hostapd_data *orig_hapd = hapd;
+
+#ifdef CONFIG_IEEE80211BE
+ if (link_id != -1) {
+ struct hostapd_data *h_hapd;
+
+ hapd = switch_link_hapd(hapd, link_id);
+ h_hapd = hostapd_find_by_sta(hapd->iface, src);
+ if (!h_hapd)
+ h_hapd = hostapd_find_by_sta(orig_hapd->iface, src);
+ if (h_hapd)
+ hapd = h_hapd;
+ } else if (hapd->conf->mld_ap) {
+ unsigned int i;
+
+ /* Search for STA on other MLO BSSs */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h =
+ hapd->iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h->bss[0];
+ struct hostapd_bss_config *hconf = h_hapd->conf;
+
+ if (!hconf->mld_ap ||
+ hconf->mld_id != hapd->conf->mld_id)
+ continue;
+
+ h_hapd = hostapd_find_by_sta(h, src);
+ if (h_hapd) {
+ hapd = h_hapd;
+ break;
+ }
+ }
+ } else {
+ hapd = hostapd_find_by_sta(hapd->iface, src);
+ }
+#else /* CONFIG_IEEE80211BE */
+ hapd = hostapd_find_by_sta(hapd->iface, src);
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!hapd) {
+ /* WLAN cases need to have an existing association, but non-WLAN
+ * cases (mainly, wired IEEE 802.1X) need to be able to process
+ * EAPOL frames from new devices that do not yet have a STA
+ * entry and as such, do not get a match in
+ * hostapd_find_by_sta(). */
+ wpa_printf(MSG_DEBUG,
+ "No STA-specific hostapd instance for EAPOL RX found - fall back to initial context");
+ hapd = orig_hapd;
+ }
+
+ ieee802_1x_receive(hapd, src, data, data_len, encrypted);
+}
+
+#endif /* HOSTAPD */
+
+
+static struct hostapd_channel_data *
+hostapd_get_mode_chan(struct hostapd_hw_modes *mode, unsigned int freq)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if ((unsigned int) chan->freq == freq)
+ return chan;
+ }
+
+ return NULL;
+}
+
+
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+ struct hostapd_iface *iface, unsigned int freq)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ if (hostapd_hw_skip_mode(iface, &iface->hw_features[i]))
+ continue;
+ chan = hostapd_get_mode_chan(&iface->hw_features[i], freq);
+ if (chan)
+ return chan;
+ }
+
+ return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan,
+ struct freq_survey *survey)
+{
+ if (!iface->chans_surveyed) {
+ chan->min_nf = survey->nf;
+ iface->lowest_nf = survey->nf;
+ } else {
+ if (dl_list_empty(&chan->survey_list))
+ chan->min_nf = survey->nf;
+ else if (survey->nf < chan->min_nf)
+ chan->min_nf = survey->nf;
+ if (survey->nf < iface->lowest_nf)
+ iface->lowest_nf = survey->nf;
+ }
+}
+
+
+static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
+ struct survey_results *survey_res)
+{
+ struct hostapd_channel_data *chan;
+ struct freq_survey *survey;
+ u64 divisor, dividend;
+
+ survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
+ list);
+ if (!survey || !survey->freq)
+ return;
+
+ chan = hostapd_get_mode_channel(iface, survey->freq);
+ if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+ survey->freq,
+ (unsigned long int) survey->channel_time,
+ (unsigned long int) survey->channel_time_busy);
+
+ if (survey->channel_time > iface->last_channel_time &&
+ survey->channel_time > survey->channel_time_busy) {
+ dividend = survey->channel_time_busy -
+ iface->last_channel_time_busy;
+ divisor = survey->channel_time - iface->last_channel_time;
+
+ iface->channel_utilization = dividend * 255 / divisor;
+ wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
+ iface->channel_utilization);
+ }
+ iface->last_channel_time = survey->channel_time;
+ iface->last_channel_time_busy = survey->channel_time_busy;
+}
+
+
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+ struct survey_results *survey_results)
+{
+ struct freq_survey *survey, *tmp;
+ struct hostapd_channel_data *chan;
+
+ if (dl_list_empty(&survey_results->survey_list)) {
+ wpa_printf(MSG_DEBUG, "No survey data received");
+ return;
+ }
+
+ if (survey_results->freq_filter) {
+ hostapd_single_channel_get_survey(iface, survey_results);
+ return;
+ }
+
+ dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+ struct freq_survey, list) {
+ chan = hostapd_get_mode_channel(iface, survey->freq);
+ if (!chan)
+ continue;
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ dl_list_del(&survey->list);
+ dl_list_add_tail(&chan->survey_list, &survey->list);
+
+ hostapd_update_nf(iface, chan, survey);
+
+ iface->chans_surveyed++;
+ }
+}
+
+
+#ifdef HOSTAPD
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+ hapd->conf->iface);
+
+ if (hapd->csa_in_progress) {
+ wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+ hapd->conf->iface);
+ hostapd_switch_channel_fallback(hapd->iface,
+ &hapd->cs_freq_params);
+ }
+}
+
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+ hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
+ hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+ hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+ hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+ hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
+ hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
+ int istatus,
+ const char *ifname,
+ const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (sta) {
+ os_free(sta->ifname_wds);
+ if (istatus == INTERFACE_ADDED)
+ sta->ifname_wds = os_strdup(ifname);
+ else
+ sta->ifname_wds = NULL;
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
+ istatus == INTERFACE_ADDED ?
+ WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
+ ifname, MAC2STR(addr));
+}
+
+
+#ifdef CONFIG_OWE
+static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
+ const u8 *peer, const u8 *ie,
+ size_t ie_len)
+{
+ u16 status;
+ struct sta_info *sta;
+ struct ieee802_11_elems elems;
+
+ if (!hapd || !hapd->wpa_auth) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context");
+ return -1;
+ }
+ if (!peer) {
+ wpa_printf(MSG_DEBUG, "OWE: Peer unknown");
+ return -1;
+ }
+ if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) {
+ wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured");
+ status = WLAN_STATUS_AKMP_NOT_VALID;
+ goto err;
+ }
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for "
+ MACSTR, MAC2STR(peer));
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto err;
+ }
+ status = owe_validate_request(hapd, peer, elems.rsn_ie,
+ elems.rsn_ie_len,
+ elems.owe_dh, elems.owe_dh_len);
+ if (status != WLAN_STATUS_SUCCESS)
+ goto err;
+
+ sta = ap_get_sta(hapd, peer);
+ if (sta) {
+ ap_sta_no_session_timeout(hapd, sta);
+ accounting_sta_stop(hapd, sta);
+
+ /*
+ * Make sure that the previously registered inactivity timer
+ * will not remove the STA immediately.
+ */
+ sta->timeout_next = STA_NULLFUNC;
+ } else {
+ sta = ap_sta_add(hapd, peer);
+ if (!sta) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto err;
+ }
+ }
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+ status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie,
+ elems.rsn_ie_len, elems.owe_dh,
+ elems.owe_dh_len);
+ if (status != WLAN_STATUS_SUCCESS)
+ ap_free_sta(hapd, sta);
+
+ return 0;
+err:
+ hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0);
+ return 0;
+}
+#endif /* CONFIG_OWE */
+
+
+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+{
+ struct hostapd_data *hapd = ctx;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int level = MSG_DEBUG;
+
+ if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
+ data->rx_mgmt.frame_len >= 24) {
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+ fc = le_to_host16(hdr->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ level = MSG_EXCESSIVE;
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
+ level = MSG_EXCESSIVE;
+ }
+
+ wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
+ event_to_string(event), event);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+ switch (event) {
+ case EVENT_MICHAEL_MIC_FAILURE:
+ michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+ break;
+ case EVENT_SCAN_RESULTS:
+ if (hapd->iface->scan_cb)
+ hapd->iface->scan_cb(hapd->iface);
+ break;
+ case EVENT_WPS_BUTTON_PUSHED:
+ hostapd_wps_button_pushed(hapd, NULL);
+ break;
+#ifdef NEED_AP_MLME
+ case EVENT_TX_STATUS:
+ switch (data->tx_status.type) {
+ case WLAN_FC_TYPE_MGMT:
+ hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.stype,
+ data->tx_status.ack,
+ data->tx_status.link_id);
+ break;
+ case WLAN_FC_TYPE_DATA:
+ hostapd_tx_status(hapd, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack);
+ break;
+ }
+ break;
+ case EVENT_EAPOL_TX_STATUS:
+ hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
+ hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
+ data->eapol_tx_status.data,
+ data->eapol_tx_status.data_len,
+ data->eapol_tx_status.ack);
+ break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ hostapd_client_poll_ok(hapd, data->client_poll.addr);
+ break;
+ case EVENT_RX_FROM_UNKNOWN:
+ hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
+ data->rx_from_unknown.addr,
+ data->rx_from_unknown.wds);
+ break;
+#endif /* NEED_AP_MLME */
+ case EVENT_RX_MGMT:
+ if (!data->rx_mgmt.frame)
+ break;
+#ifdef NEED_AP_MLME
+ hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+#else /* NEED_AP_MLME */
+ hostapd_action_rx(hapd, &data->rx_mgmt);
+#endif /* NEED_AP_MLME */
+ break;
+ case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+ data->rx_probe_req.ie == NULL)
+ break;
+ hostapd_probe_req_rx(hapd, data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len,
+ data->rx_probe_req.ssi_signal);
+ break;
+ case EVENT_NEW_STA:
+ hostapd_event_new_sta(hapd, data->new_sta.addr);
+ break;
+ case EVENT_EAPOL_RX:
+ hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
+ data->eapol_rx.data,
+ data->eapol_rx.data_len,
+ data->eapol_rx.encrypted,
+ data->eapol_rx.link_id);
+ break;
+ case EVENT_ASSOC:
+ if (!data)
+ return;
+ hostapd_notif_assoc(hapd, data->assoc_info.addr,
+ data->assoc_info.req_ies,
+ data->assoc_info.req_ies_len,
+ data->assoc_info.reassoc);
+ break;
+#ifdef CONFIG_OWE
+ case EVENT_UPDATE_DH:
+ if (!data)
+ return;
+ hostapd_notif_update_dh_ie(hapd, data->update_dh.peer,
+ data->update_dh.ie,
+ data->update_dh.ie_len);
+ break;
+#endif /* CONFIG_OWE */
+ case EVENT_DISASSOC:
+ if (data)
+ hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
+ break;
+ case EVENT_DEAUTH:
+ if (data)
+ hostapd_notif_disassoc(hapd, data->deauth_info.addr);
+ break;
+ case EVENT_STATION_LOW_ACK:
+ if (!data)
+ break;
+ hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+ break;
+ case EVENT_AUTH:
+ hostapd_notif_auth(hapd, &data->auth);
+ break;
+ case EVENT_CH_SWITCH_STARTED:
+ case EVENT_CH_SWITCH:
+ if (!data)
+ break;
+ hostapd_event_ch_switch(hapd, data->ch_switch.freq,
+ data->ch_switch.ht_enabled,
+ data->ch_switch.ch_offset,
+ data->ch_switch.ch_width,
+ data->ch_switch.cf1,
+ data->ch_switch.cf2,
+ data->ch_switch.punct_bitmap,
+ event == EVENT_CH_SWITCH);
+ break;
+ case EVENT_CONNECT_FAILED_REASON:
+ if (!data)
+ break;
+ hostapd_event_connect_failed_reason(
+ hapd, data->connect_failed_reason.addr,
+ data->connect_failed_reason.code);
+ break;
+ case EVENT_SURVEY:
+ hostapd_event_get_survey(hapd->iface, &data->survey_results);
+ break;
+#ifdef NEED_AP_MLME
+ case EVENT_INTERFACE_UNAVAILABLE:
+ hostapd_event_iface_unavailable(hapd);
+ break;
+ case EVENT_DFS_RADAR_DETECTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_PRE_CAC_EXPIRED:
+ if (!data)
+ break;
+ hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_FINISHED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_ABORTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_NOP_FINISHED:
+ if (!data)
+ break;
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ /* channel list changed (regulatory?), update channel list */
+ /* TODO: check this. hostapd_get_hw_features() initializes
+ * too much stuff. */
+ /* hostapd_get_hw_features(hapd->iface); */
+ hostapd_channel_list_updated(
+ hapd->iface, data->channel_list_changed.initiator);
+ break;
+ case EVENT_DFS_CAC_STARTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+ break;
+#endif /* NEED_AP_MLME */
+ case EVENT_INTERFACE_ENABLED:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+ if (hapd->disabled && hapd->started) {
+ hapd->disabled = 0;
+ /*
+ * Try to re-enable interface if the driver stopped it
+ * when the interface got disabled.
+ */
+ if (hapd->wpa_auth)
+ wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+ else
+ hostapd_reconfig_encryption(hapd);
+ hapd->reenable_beacon = 1;
+ ieee802_11_set_beacon(hapd);
+#ifdef NEED_AP_MLME
+ } else if (hapd->disabled && hapd->iface->cac_started) {
+ wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
+ hostapd_handle_dfs(hapd->iface);
+#endif /* NEED_AP_MLME */
+ }
+ break;
+ case EVENT_INTERFACE_DISABLED:
+ hostapd_free_stas(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+ hapd->disabled = 1;
+ break;
+#ifdef CONFIG_ACS
+ case EVENT_ACS_CHANNEL_SELECTED:
+ hostapd_acs_channel_selected(hapd,
+ &data->acs_selected_channels);
+ break;
+#endif /* CONFIG_ACS */
+ case EVENT_STATION_OPMODE_CHANGED:
+ hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
+ data->sta_opmode.smps_mode,
+ data->sta_opmode.chan_width,
+ data->sta_opmode.rx_nss);
+ break;
+ case EVENT_WDS_STA_INTERFACE_STATUS:
+ hostapd_event_wds_sta_interface_status(
+ hapd, data->wds_sta_interface.istatus,
+ data->wds_sta_interface.ifname,
+ data->wds_sta_interface.sta_addr);
+ break;
+#ifdef CONFIG_IEEE80211AX
+ case EVENT_BSS_COLOR_COLLISION:
+ /* The BSS color is shared amongst all BBSs on a specific phy.
+ * Therefore we always start the color change on the primary
+ * BSS. */
+ wpa_printf(MSG_DEBUG, "BSS color collision on %s",
+ hapd->conf->iface);
+ hostapd_switch_color(hapd->iface->bss[0],
+ data->bss_color_collision.bitmap);
+ break;
+ case EVENT_CCA_STARTED_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA started on on %s",
+ hapd->conf->iface);
+ break;
+ case EVENT_CCA_ABORTED_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA aborted on on %s",
+ hapd->conf->iface);
+ hostapd_cleanup_cca_params(hapd);
+ break;
+ case EVENT_CCA_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ hapd->conf->iface);
+ hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+ hostapd_cleanup_cca_params(hapd);
+ break;
+#endif /* CONFIG_IEEE80211AX */
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+ break;
+ }
+}
+
+
+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+{
+ struct hapd_interfaces *interfaces = ctx;
+ struct hostapd_data *hapd;
+
+ if (event != EVENT_INTERFACE_STATUS)
+ return;
+
+ hapd = hostapd_get_iface(interfaces, data->interface_status.ifname);
+ if (hapd && hapd->driver && hapd->driver->get_ifindex &&
+ hapd->drv_priv) {
+ unsigned int ifindex;
+
+ ifindex = hapd->driver->get_ifindex(hapd->drv_priv);
+ if (ifindex != data->interface_status.ifindex) {
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "interface status ifindex %d mismatch (%d)",
+ ifindex, data->interface_status.ifindex);
+ return;
+ }
+ }
+ if (hapd)
+ wpa_supplicant_event(hapd, event, data);
+}
+
+#endif /* HOSTAPD */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eap_user_db.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eap_user_db.c
new file mode 100644
index 0000000..a510ee3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eap_user_db.c
@@ -0,0 +1,290 @@
+/*
+ * hostapd / EAP user database
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap_methods.h"
+#include "eap_server/eap.h"
+#include "ap_config.h"
+#include "hostapd.h"
+
+#ifdef CONFIG_SQLITE
+
+static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
+{
+ char *buf, *start;
+ int num_methods;
+
+ buf = os_strdup(methods);
+ if (buf == NULL)
+ return;
+
+ os_memset(&user->methods, 0, sizeof(user->methods));
+ num_methods = 0;
+ start = buf;
+ while (*start) {
+ char *pos3 = os_strchr(start, ',');
+ if (pos3)
+ *pos3++ = '\0';
+ user->methods[num_methods].method =
+ eap_server_get_type(start,
+ &user->methods[num_methods].vendor);
+ if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
+ user->methods[num_methods].method == EAP_TYPE_NONE) {
+ if (os_strcmp(start, "TTLS-PAP") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_PAP;
+ goto skip_eap;
+ }
+ if (os_strcmp(start, "TTLS-CHAP") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+ goto skip_eap;
+ }
+ if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+ goto skip_eap;
+ }
+ if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+ goto skip_eap;
+ }
+ wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
+ start);
+ os_free(buf);
+ return;
+ }
+
+ num_methods++;
+ if (num_methods >= EAP_MAX_METHODS)
+ break;
+ skip_eap:
+ if (pos3 == NULL)
+ break;
+ start = pos3;
+ }
+
+ os_free(buf);
+}
+
+
+static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct hostapd_eap_user *user = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "password") == 0 && argv[i]) {
+ bin_clear_free(user->password, user->password_len);
+ user->password_len = os_strlen(argv[i]);
+ user->password = (u8 *) os_strdup(argv[i]);
+ user->next = (void *) 1;
+ } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
+ set_user_methods(user, argv[i]);
+ } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+ user->remediation = strlen(argv[i]) > 0;
+ } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
+ user->t_c_timestamp = strtol(argv[i], NULL, 10);
+ }
+ }
+
+ return 0;
+}
+
+
+static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct hostapd_eap_user *user = ctx;
+ int i, id = -1, methods = -1;
+ size_t len;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "identity") == 0 && argv[i])
+ id = i;
+ else if (os_strcmp(col[i], "methods") == 0 && argv[i])
+ methods = i;
+ }
+
+ if (id < 0 || methods < 0)
+ return 0;
+
+ len = os_strlen(argv[id]);
+ if (len <= user->identity_len &&
+ os_memcmp(argv[id], user->identity, len) == 0 &&
+ (user->password == NULL || len > user->password_len)) {
+ bin_clear_free(user->password, user->password_len);
+ user->password_len = os_strlen(argv[id]);
+ user->password = (u8 *) os_strdup(argv[id]);
+ user->next = (void *) 1;
+ set_user_methods(user, argv[methods]);
+ }
+
+ return 0;
+}
+
+
+static const struct hostapd_eap_user *
+eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2)
+{
+ sqlite3 *db;
+ struct hostapd_eap_user *user = NULL;
+ char id_str[256], cmd[300];
+ size_t i;
+ int res;
+
+ if (identity_len >= sizeof(id_str)) {
+ wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
+ __func__, (int) identity_len,
+ (int) (sizeof(id_str)));
+ return NULL;
+ }
+ os_memcpy(id_str, identity, identity_len);
+ id_str[identity_len] = '\0';
+ for (i = 0; i < identity_len; i++) {
+ if (id_str[i] >= 'a' && id_str[i] <= 'z')
+ continue;
+ if (id_str[i] >= 'A' && id_str[i] <= 'Z')
+ continue;
+ if (id_str[i] >= '0' && id_str[i] <= '9')
+ continue;
+ if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
+ id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
+ id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
+ id_str[i] == '=' || id_str[i] == ' ')
+ continue;
+ wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
+ return NULL;
+ }
+
+ bin_clear_free(hapd->tmp_eap_user.identity,
+ hapd->tmp_eap_user.identity_len);
+ bin_clear_free(hapd->tmp_eap_user.password,
+ hapd->tmp_eap_user.password_len);
+ os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
+ hapd->tmp_eap_user.phase2 = phase2;
+ hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
+ if (hapd->tmp_eap_user.identity == NULL)
+ return NULL;
+ os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
+ hapd->tmp_eap_user.identity_len = identity_len;
+
+ if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
+ wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
+ hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd),
+ "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+ id_str, phase2);
+ if (os_snprintf_error(sizeof(cmd), res))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+ if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
+ SQLITE_OK) {
+ wpa_printf(MSG_DEBUG,
+ "DB: Failed to complete SQL operation: %s db: %s",
+ sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
+ } else if (hapd->tmp_eap_user.next)
+ user = &hapd->tmp_eap_user;
+
+ if (user == NULL && !phase2) {
+ os_snprintf(cmd, sizeof(cmd),
+ "SELECT identity,methods FROM wildcards;");
+ wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+ if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
+ NULL) != SQLITE_OK) {
+ wpa_printf(MSG_DEBUG,
+ "DB: Failed to complete SQL operation: %s db: %s",
+ sqlite3_errmsg(db),
+ hapd->conf->eap_user_sqlite);
+ } else if (hapd->tmp_eap_user.next) {
+ user = &hapd->tmp_eap_user;
+ os_free(user->identity);
+ user->identity = user->password;
+ user->identity_len = user->password_len;
+ user->password = NULL;
+ user->password_len = 0;
+ }
+ }
+
+fail:
+ sqlite3_close(db);
+
+ return user;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2)
+{
+ const struct hostapd_bss_config *conf = hapd->conf;
+ struct hostapd_eap_user *user = conf->eap_user;
+
+#ifdef CONFIG_WPS
+ if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+ static struct hostapd_eap_user wsc_enrollee;
+ os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+ wsc_enrollee.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_enrollee.methods[0].vendor);
+ return &wsc_enrollee;
+ }
+
+ if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+ static struct hostapd_eap_user wsc_registrar;
+ os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+ wsc_registrar.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_registrar.methods[0].vendor);
+ wsc_registrar.password = (u8 *) conf->ap_pin;
+ wsc_registrar.password_len = conf->ap_pin ?
+ os_strlen(conf->ap_pin) : 0;
+ return &wsc_registrar;
+ }
+#endif /* CONFIG_WPS */
+
+ while (user) {
+ if (!phase2 && user->identity == NULL) {
+ /* Wildcard match */
+ break;
+ }
+
+ if (user->phase2 == !!phase2 && user->wildcard_prefix &&
+ identity_len >= user->identity_len &&
+ os_memcmp(user->identity, identity, user->identity_len) ==
+ 0) {
+ /* Wildcard prefix match */
+ break;
+ }
+
+ if (user->phase2 == !!phase2 &&
+ user->identity_len == identity_len &&
+ os_memcmp(user->identity, identity, identity_len) == 0)
+ break;
+ user = user->next;
+ }
+
+#ifdef CONFIG_SQLITE
+ if (user == NULL && conf->eap_user_sqlite) {
+ return eap_user_sqlite_get(hapd, identity, identity_len,
+ phase2);
+ }
+#endif /* CONFIG_SQLITE */
+
+ return user;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.c
new file mode 100644
index 0000000..aba901e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802 OUI Extended EtherType 88-B7
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "eth_p_oui.h"
+
+/*
+ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
+ * EtherType 88-B7. This file implements this with OUI 00:13:74 and
+ * vendor-specific subtype 0x0001.
+ */
+static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
+
+struct eth_p_oui_iface {
+ struct dl_list list;
+ char ifname[IFNAMSIZ + 1];
+ struct l2_packet_data *l2;
+ struct dl_list receiver;
+};
+
+struct eth_p_oui_ctx {
+ struct dl_list list;
+ struct eth_p_oui_iface *iface;
+ /* all data needed to deliver and unregister */
+ u8 oui_suffix; /* last byte of OUI */
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+};
+
+
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len)
+{
+ ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
+ ctx->oui_suffix, buf, len);
+}
+
+
+static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct eth_p_oui_iface *iface = ctx;
+ struct eth_p_oui_ctx *receiver;
+ const struct l2_ethhdr *ethhdr;
+
+ if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
+ /* too short packet */
+ return;
+ }
+
+ ethhdr = (struct l2_ethhdr *) buf;
+ /* trim eth_hdr from buf and len */
+ buf += sizeof(*ethhdr);
+ len -= sizeof(*ethhdr);
+
+ /* verify OUI and vendor-specific subtype match */
+ if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
+ return;
+ buf += sizeof(global_oui);
+ len -= sizeof(global_oui);
+
+ dl_list_for_each(receiver, &iface->receiver,
+ struct eth_p_oui_ctx, list) {
+ if (buf[0] != receiver->oui_suffix)
+ continue;
+
+ eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
+ buf + 1, len - 1);
+ }
+}
+
+
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx)
+{
+ struct eth_p_oui_iface *iface;
+ struct eth_p_oui_ctx *receiver;
+ int found = 0;
+ struct hapd_interfaces *interfaces;
+
+ receiver = os_zalloc(sizeof(*receiver));
+ if (!receiver)
+ goto err;
+
+ receiver->oui_suffix = oui_suffix;
+ receiver->rx_callback = rx_callback;
+ receiver->rx_callback_ctx = rx_callback_ctx;
+
+ interfaces = hapd->iface->interfaces;
+
+ dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
+ list) {
+ if (os_strcmp(iface->ifname, ifname) != 0)
+ continue;
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ iface = os_zalloc(sizeof(*iface));
+ if (!iface)
+ goto err;
+
+ os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
+ iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
+ iface, 1);
+ if (!iface->l2) {
+ os_free(iface);
+ goto err;
+ }
+ dl_list_init(&iface->receiver);
+
+ dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
+ }
+
+ dl_list_add_tail(&iface->receiver, &receiver->list);
+ receiver->iface = iface;
+
+ return receiver;
+err:
+ os_free(receiver);
+ return NULL;
+}
+
+
+void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
+{
+ struct eth_p_oui_iface *iface;
+
+ if (!ctx)
+ return;
+
+ iface = ctx->iface;
+
+ dl_list_del(&ctx->list);
+ os_free(ctx);
+
+ if (dl_list_empty(&iface->receiver)) {
+ dl_list_del(&iface->list);
+ l2_packet_deinit(iface->l2);
+ os_free(iface);
+ }
+}
+
+
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len)
+{
+ struct eth_p_oui_iface *iface = ctx->iface;
+ u8 *packet, *p;
+ size_t packet_len;
+ int ret;
+ struct l2_ethhdr *ethhdr;
+
+ packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
+ packet = os_zalloc(packet_len);
+ if (!packet)
+ return -1;
+ p = packet;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
+ os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+ ethhdr->h_proto = host_to_be16(ETH_P_OUI);
+ p += sizeof(*ethhdr);
+
+ os_memcpy(p, global_oui, sizeof(global_oui));
+ p[sizeof(global_oui)] = ctx->oui_suffix;
+ p += sizeof(global_oui) + 1;
+
+ os_memcpy(p, buf, len);
+
+ ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
+ os_free(packet);
+ return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.h
new file mode 100644
index 0000000..466fdc3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.h
@@ -0,0 +1,28 @@
+/*
+ * hostapd / IEEE 802 OUI Extended Ethertype
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ETH_P_OUI_H
+#define ETH_P_OUI_H
+
+struct eth_p_oui_ctx;
+struct hostapd_data;
+
+/* rx_callback only gets payload after OUI passed as buf */
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx);
+void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len);
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len);
+
+#endif /* ETH_P_OUI_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.c
new file mode 100644
index 0000000..d64fb8c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.c
@@ -0,0 +1,654 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dhcp.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+#include "fils_hlp.h"
+
+
+static be16 ip_checksum(const void *buf, size_t len)
+{
+ u32 sum = 0;
+ const u16 *pos;
+
+ for (pos = buf; len >= 2; len -= 2)
+ sum += ntohs(*pos++);
+ if (len)
+ sum += ntohs(*pos << 8);
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += sum >> 16;
+ return htons(~sum);
+}
+
+
+static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
+ struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
+{
+ u8 *pos, *end;
+ struct dhcp_data *dhcp;
+ struct sockaddr_in addr;
+ ssize_t res;
+ const u8 *server_id = NULL;
+
+ if (!sta->hlp_dhcp_discover) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No pending HLP DHCPDISCOVER available");
+ return -1;
+ }
+
+ /* Convert to DHCPREQUEST, remove rapid commit option, replace requested
+ * IP address option with yiaddr. */
+ pos = wpabuf_mhead(sta->hlp_dhcp_discover);
+ end = pos + wpabuf_len(sta->hlp_dhcp_discover);
+ dhcp = (struct dhcp_data *) pos;
+ pos = (u8 *) (dhcp + 1);
+ pos += 4; /* skip magic */
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_MSG_TYPE:
+ if (olen > 0)
+ *pos = DHCPREQUEST;
+ break;
+ case DHCP_OPT_RAPID_COMMIT:
+ case DHCP_OPT_REQUESTED_IP_ADDRESS:
+ case DHCP_OPT_SERVER_ID:
+ /* Remove option */
+ pos -= 2;
+ os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
+ end -= 2 + olen;
+ olen = 0;
+ break;
+ }
+ pos += olen;
+ }
+ if (pos >= end || *pos != DHCP_OPT_END) {
+ wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
+ return -1;
+ }
+ sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
+
+ /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
+ pos = (u8 *) (dhcpoffer + 1);
+ end = dhcpofferend;
+ pos += 4; /* skip magic */
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_SERVER_ID:
+ server_id = pos - 2;
+ break;
+ }
+ pos += olen;
+ }
+
+ if (wpabuf_resize(&sta->hlp_dhcp_discover,
+ 6 + 1 + (server_id ? 2 + server_id[1] : 0)))
+ return -1;
+ if (server_id)
+ wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
+ 2 + server_id[1]);
+ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
+ wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
+ wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
+ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+ addr.sin_port = htons(hapd->conf->dhcp_server_port);
+ res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
+ wpabuf_len(sta->hlp_dhcp_discover), 0,
+ (const struct sockaddr *) &addr, sizeof(addr));
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+ strerror(errno));
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FILS: Acting as DHCP rapid commit proxy for %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+ sta->fils_dhcp_rapid_commit_proxy = 1;
+ return 0;
+}
+
+
+static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct hostapd_data *hapd = sock_ctx;
+ struct sta_info *sta;
+ u8 buf[1500], *pos, *end, *end_opt = NULL;
+ struct dhcp_data *dhcp;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+ ssize_t res;
+ u8 msgtype = 0;
+ int rapid_commit = 0;
+ struct ip *iph;
+ struct udphdr *udph;
+ struct wpabuf *resp;
+ const u8 *rpos;
+ size_t left, len;
+
+ addr_len = sizeof(addr);
+ res = recvfrom(sd, buf, sizeof(buf), 0,
+ (struct sockaddr *) &addr, &addr_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
+ strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
+ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
+ if ((size_t) res < sizeof(*dhcp))
+ return;
+ dhcp = (struct dhcp_data *) buf;
+ if (dhcp->op != 2)
+ return; /* Not a BOOTREPLY */
+ if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - DHCP response to unknown relay address 0x%x",
+ dhcp->relay_ip);
+ return;
+ }
+ dhcp->relay_ip = 0;
+ pos = (u8 *) (dhcp + 1);
+ end = &buf[res];
+
+ if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
+ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
+ return;
+ }
+ pos += 4;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
+ pos, end - pos);
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_MSG_TYPE:
+ if (olen > 0)
+ msgtype = pos[0];
+ break;
+ case DHCP_OPT_RAPID_COMMIT:
+ rapid_commit = 1;
+ break;
+ }
+ pos += olen;
+ }
+ if (pos < end && *pos == DHCP_OPT_END)
+ end_opt = pos;
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
+ MACSTR ")",
+ msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
+
+ sta = ap_get_sta(hapd, dhcp->hw_addr);
+ if (!sta || !sta->fils_pending_assoc_req) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No pending HLP DHCP exchange with hw_addr "
+ MACSTR, MAC2STR(dhcp->hw_addr));
+ return;
+ }
+
+ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
+ !rapid_commit) {
+ /* Use hostapd to take care of 4-message exchange and convert
+ * the final DHCPACK to rapid commit version. */
+ if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
+ return;
+ /* failed, so send the server response as-is */
+ } else if (msgtype != DHCPACK) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
+ }
+
+ pos = buf;
+ resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
+ sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
+ if (!resp)
+ return;
+ wpabuf_put_data(resp, sta->addr, ETH_ALEN);
+ wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
+ wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
+ wpabuf_put_be16(resp, ETH_P_IP);
+ iph = wpabuf_put(resp, sizeof(*iph));
+ iph->ip_v = 4;
+ iph->ip_hl = sizeof(*iph) / 4;
+ iph->ip_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
+ iph->ip_ttl = 1;
+ iph->ip_p = 17; /* UDP */
+ iph->ip_src.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+ iph->ip_dst.s_addr = dhcp->client_ip;
+ iph->ip_sum = ip_checksum(iph, sizeof(*iph));
+ udph = wpabuf_put(resp, sizeof(*udph));
+ udph->uh_sport = htons(DHCP_SERVER_PORT);
+ udph->uh_dport = htons(DHCP_CLIENT_PORT);
+ udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
+ udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
+ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
+ !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
+ /* Add rapid commit option */
+ wpabuf_put_data(resp, pos, end_opt - pos);
+ wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
+ wpabuf_put_u8(resp, 0);
+ wpabuf_put_data(resp, end_opt, end - end_opt);
+ } else {
+ wpabuf_put_data(resp, pos, end - pos);
+ }
+ if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
+ 2 * wpabuf_len(resp) / 255 + 100)) {
+ wpabuf_free(resp);
+ return;
+ }
+
+ rpos = wpabuf_head(resp);
+ left = wpabuf_len(resp);
+
+ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
+ if (left <= 254)
+ len = 1 + left;
+ else
+ len = 255;
+ wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+ /* Destination MAC Address, Source MAC Address, HLP Packet.
+ * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
+ * when LPD is used). */
+ wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
+ rpos += len - 1;
+ left -= len - 1;
+ while (left) {
+ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
+ len = left > 255 ? 255 : left;
+ wpabuf_put_u8(sta->fils_hlp_resp, len);
+ wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
+ rpos += len;
+ left -= len;
+ }
+ wpabuf_free(resp);
+
+ if (sta->fils_drv_assoc_finish)
+ hostapd_notify_assoc_fils_finish(hapd, sta);
+ else
+ fils_hlp_finish_assoc(hapd, sta);
+}
+
+
+static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *msg, size_t len)
+{
+ const struct dhcp_data *dhcp;
+ struct wpabuf *dhcp_buf;
+ struct dhcp_data *dhcp_msg;
+ u8 msgtype = 0;
+ int rapid_commit = 0;
+ const u8 *pos = msg, *end;
+ struct sockaddr_in addr;
+ ssize_t res;
+
+ if (len < sizeof(*dhcp))
+ return 0;
+ dhcp = (const struct dhcp_data *) pos;
+ end = pos + len;
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
+ dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
+ ntohl(dhcp->xid));
+ pos += sizeof(*dhcp);
+ if (dhcp->op != 1)
+ return 0; /* Not a BOOTREQUEST */
+
+ if (end - pos < 4)
+ return 0;
+ if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
+ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
+ return 0;
+ }
+ pos += 4;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_MSG_TYPE:
+ if (olen > 0)
+ msgtype = pos[0];
+ break;
+ case DHCP_OPT_RAPID_COMMIT:
+ rapid_commit = 1;
+ break;
+ }
+ pos += olen;
+ }
+
+ wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
+ if (msgtype != DHCPDISCOVER)
+ return 0;
+
+ if (hapd->conf->dhcp_server.af != AF_INET ||
+ hapd->conf->dhcp_server.u.v4.s_addr == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - no DHCPv4 server configured - drop request");
+ return 0;
+ }
+
+ if (hapd->conf->own_ip_addr.af != AF_INET ||
+ hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
+ return 0;
+ }
+
+ if (hapd->dhcp_sock < 0) {
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to open DHCP socket: %s",
+ strerror(errno));
+ return 0;
+ }
+
+ if (hapd->conf->dhcp_relay_port) {
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr =
+ hapd->conf->own_ip_addr.u.v4.s_addr;
+ addr.sin_port = htons(hapd->conf->dhcp_relay_port);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to bind DHCP socket: %s",
+ strerror(errno));
+ close(s);
+ return 0;
+ }
+ }
+ if (eloop_register_sock(s, EVENT_TYPE_READ,
+ fils_dhcp_handler, NULL, hapd)) {
+ close(s);
+ return 0;
+ }
+
+ hapd->dhcp_sock = s;
+ }
+
+ dhcp_buf = wpabuf_alloc(len);
+ if (!dhcp_buf)
+ return 0;
+ dhcp_msg = wpabuf_put(dhcp_buf, len);
+ os_memcpy(dhcp_msg, msg, len);
+ dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+ addr.sin_port = htons(hapd->conf->dhcp_server_port);
+ res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
+ (const struct sockaddr *) &addr, sizeof(addr));
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+ strerror(errno));
+ wpabuf_free(dhcp_buf);
+ /* Close the socket to try to recover from error */
+ eloop_unregister_read_sock(hapd->dhcp_sock);
+ close(hapd->dhcp_sock);
+ hapd->dhcp_sock = -1;
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+ rapid_commit);
+ if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
+ /* Store a copy of the DHCPDISCOVER for rapid commit proxying
+ * purposes if the server does not support the rapid commit
+ * option. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Store DHCPDISCOVER for rapid commit proxy");
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = dhcp_buf;
+ } else {
+ wpabuf_free(dhcp_buf);
+ }
+
+ return 1;
+}
+
+
+static int fils_process_hlp_udp(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *dst,
+ const u8 *pos, size_t len)
+{
+ const struct ip *iph;
+ const struct udphdr *udph;
+ u16 sport, dport, ulen;
+
+ if (len < sizeof(*iph) + sizeof(*udph))
+ return 0;
+ iph = (const struct ip *) pos;
+ udph = (const struct udphdr *) (iph + 1);
+ sport = ntohs(udph->uh_sport);
+ dport = ntohs(udph->uh_dport);
+ ulen = ntohs(udph->uh_ulen);
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
+ sport, dport, ulen, ntohs(udph->uh_sum));
+ /* TODO: Check UDP checksum */
+ if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
+ return 0;
+
+ if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
+ return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
+ ulen - sizeof(*udph));
+ }
+
+ return 0;
+}
+
+
+static int fils_process_hlp_ip(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *dst,
+ const u8 *pos, size_t len)
+{
+ const struct ip *iph;
+ uint16_t ip_len;
+
+ if (len < sizeof(*iph))
+ return 0;
+ iph = (const struct ip *) pos;
+ if (ip_checksum(iph, sizeof(*iph)) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
+ return 0;
+ }
+ ip_len = ntohs(iph->ip_len);
+ if (ip_len > len)
+ return 0;
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
+ iph->ip_src.s_addr, iph->ip_dst.s_addr, iph->ip_p);
+ switch (iph->ip_p) {
+ case 17:
+ return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+ default:
+ return 0;
+ }
+}
+
+
+static int fils_process_hlp_req(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *pos, size_t len)
+{
+ const u8 *pkt, *end;
+
+ wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
+ " src=" MACSTR " len=%u)",
+ MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
+ (unsigned int) len);
+ if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Ignore HLP request with unexpected source address"
+ MACSTR, MAC2STR(pos + ETH_ALEN));
+ return 0;
+ }
+
+ end = pos + len;
+ pkt = pos + 2 * ETH_ALEN;
+ if (end - pkt >= 6 &&
+ os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
+ pkt += 6; /* Remove SNAP/LLC header */
+ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
+
+ if (end - pkt < 2)
+ return 0;
+
+ switch (WPA_GET_BE16(pkt)) {
+ case ETH_P_IP:
+ return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
+ end - pkt - 2);
+ default:
+ return 0;
+ }
+}
+
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, int left)
+{
+ const u8 *end = pos + left;
+ u8 *tmp, *tmp_pos;
+ int ret = 0;
+
+ if (sta->fils_pending_assoc_req &&
+ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
+ /* Do not process FILS HLP request again if the station
+ * retransmits (Re)Association Request frame before the previous
+ * HLP response has either been received or timed out. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Do not relay another HLP request from "
+ MACSTR
+ " before processing of the already pending one has been completed",
+ MAC2STR(sta->addr));
+ return 1;
+ }
+
+ /* Old DHCPDISCOVER is not needed anymore, if it was still pending */
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+ sta->fils_dhcp_rapid_commit_proxy = 0;
+
+ /* Check if there are any FILS HLP Container elements */
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos)
+ return 0;
+ if (pos[0] == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 + 2 * ETH_ALEN &&
+ pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
+ break;
+ pos += 2 + pos[1];
+ }
+ if (end - pos < 2)
+ return 0; /* No FILS HLP Container elements */
+
+ tmp = os_malloc(end - pos);
+ if (!tmp)
+ return 0;
+
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos ||
+ pos[0] != WLAN_EID_EXTENSION ||
+ pos[1] < 1 + 2 * ETH_ALEN ||
+ pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
+ break;
+ tmp_pos = tmp;
+ os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
+ tmp_pos += pos[1] - 1;
+ pos += 2 + pos[1];
+
+ /* Add possible fragments */
+ while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
+ 2 + pos[1] <= end - pos) {
+ os_memcpy(tmp_pos, pos + 2, pos[1]);
+ tmp_pos += pos[1];
+ pos += 2 + pos[1];
+ }
+
+ if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
+ ret = 1;
+ }
+
+ os_free(tmp);
+
+ return ret;
+}
+
+
+void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->dhcp_sock >= 0) {
+ eloop_unregister_read_sock(hapd->dhcp_sock);
+ close(hapd->dhcp_sock);
+ hapd->dhcp_sock = -1;
+ }
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.h
new file mode 100644
index 0000000..e14a6bf
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.h
@@ -0,0 +1,27 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FILS_HLP_H
+#define FILS_HLP_H
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, int left);
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_FILS */
+
+static inline void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_FILS */
+
+#endif /* FILS_HLP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.c
new file mode 100644
index 0000000..3d94407
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.c
@@ -0,0 +1,718 @@
+/*
+ * Generic advertisement service (GAS) query (hostapd)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+/* GAS query wait-time / duration in ms */
+#define GAS_QUERY_WAIT_TIME_INITIAL 1000
+#define GAS_QUERY_WAIT_TIME_COMEBACK 150
+
+#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+ struct dl_list list;
+ struct gas_query_ap *gas;
+ u8 addr[ETH_ALEN];
+ u8 dialog_token;
+ u8 next_frag_id;
+ unsigned int wait_comeback:1;
+ unsigned int offchannel_tx_started:1;
+ unsigned int retry:1;
+ int freq;
+ u16 status_code;
+ struct wpabuf *req;
+ struct wpabuf *adv_proto;
+ struct wpabuf *resp;
+ struct os_reltime last_oper;
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+ void *ctx;
+ u8 sa[ETH_ALEN];
+};
+
+/**
+ * struct gas_query_ap - Internal GAS query data
+ */
+struct gas_query_ap {
+ struct hostapd_data *hapd;
+ void *msg_ctx;
+ struct dl_list pending; /* struct gas_query_pending */
+ struct gas_query_pending *current;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+ struct gas_query_pending *query);
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+ struct os_reltime now, res;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, last, &res);
+ return res.sec * 1000 + res.usec / 1000;
+}
+
+
+/**
+ * gas_query_ap_init - Initialize GAS query component
+ * @hapd: Pointer to hostapd data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+ void *msg_ctx)
+{
+ struct gas_query_ap *gas;
+
+ gas = os_zalloc(sizeof(*gas));
+ if (!gas)
+ return NULL;
+
+ gas->hapd = hapd;
+ gas->msg_ctx = msg_ctx;
+ dl_list_init(&gas->pending);
+
+ return gas;
+}
+
+
+static const char * gas_result_txt(enum gas_query_ap_result result)
+{
+ switch (result) {
+ case GAS_QUERY_AP_SUCCESS:
+ return "SUCCESS";
+ case GAS_QUERY_AP_FAILURE:
+ return "FAILURE";
+ case GAS_QUERY_AP_TIMEOUT:
+ return "TIMEOUT";
+ case GAS_QUERY_AP_PEER_ERROR:
+ return "PEER_ERROR";
+ case GAS_QUERY_AP_INTERNAL_ERROR:
+ return "INTERNAL_ERROR";
+ case GAS_QUERY_AP_DELETED_AT_DEINIT:
+ return "DELETED_AT_DEINIT";
+ }
+
+ return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+ if (del_list)
+ dl_list_del(&query->list);
+
+ wpabuf_free(query->req);
+ wpabuf_free(query->adv_proto);
+ wpabuf_free(query->resp);
+ os_free(query);
+}
+
+
+static void gas_query_done(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ enum gas_query_ap_result result)
+{
+ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+ " dialog_token=%u freq=%d status_code=%u result=%s",
+ MAC2STR(query->addr), query->dialog_token, query->freq,
+ query->status_code, gas_result_txt(result));
+ if (gas->current == query)
+ gas->current = NULL;
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+ dl_list_del(&query->list);
+ query->cb(query->ctx, query->addr, query->dialog_token, result,
+ query->adv_proto, query->resp, query->status_code);
+ gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_ap_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_ap_deinit(struct gas_query_ap *gas)
+{
+ struct gas_query_pending *query, *next;
+
+ if (gas == NULL)
+ return;
+
+ dl_list_for_each_safe(query, next, &gas->pending,
+ struct gas_query_pending, list)
+ gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
+
+ os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ q->dialog_token == dialog_token)
+ return q;
+ }
+ return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+ size_t len)
+{
+ if (wpabuf_resize(&query->resp, len) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+ return -1;
+ }
+ wpabuf_put_data(query->resp, data, len);
+ return 0;
+}
+
+
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+ const u8 *data, size_t data_len, int ok)
+{
+ struct gas_query_pending *query;
+ int dur;
+
+ if (!gas || !gas->current) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
+ " ok=%d - no query in progress", MAC2STR(dst), ok);
+ return;
+ }
+
+ query = gas->current;
+
+ dur = ms_from_time(&query->last_oper);
+ wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
+ " ok=%d query=%p dialog_token=%u dur=%d ms",
+ MAC2STR(dst), ok, query, query->dialog_token, dur);
+ if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+ return;
+ }
+ os_get_reltime(&query->last_oper);
+
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ if (!ok) {
+ wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+ eloop_register_timeout(0, 250000, gas_query_timeout,
+ gas, query);
+ } else {
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+ }
+ if (query->wait_comeback && !query->retry) {
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout,
+ gas, query);
+ eloop_register_timeout(
+ 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
+ gas_query_rx_comeback_timeout, gas, query);
+ }
+}
+
+
+static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ return sta && (sta->flags & WLAN_STA_MFP);
+}
+
+
+static int gas_query_tx(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ struct wpabuf *req, unsigned int wait_time)
+{
+ int res, prot = pmf_in_use(gas->hapd, query->addr);
+
+ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+ "freq=%d prot=%d using src addr " MACSTR,
+ MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
+ query->freq, prot, MAC2STR(query->sa));
+ if (prot) {
+ u8 *categ = wpabuf_mhead_u8(req);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+ }
+ os_get_reltime(&query->last_oper);
+ res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
+ query->addr, wpabuf_head(req),
+ wpabuf_len(req));
+ return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
+ struct gas_query_pending *query)
+{
+ struct wpabuf *req;
+ unsigned int wait_time;
+
+ req = gas_build_comeback_req(query->dialog_token);
+ if (req == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ wait_time = (query->retry || !query->offchannel_tx_started) ?
+ GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
+
+ if (gas_query_tx(gas, query, req, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ }
+
+ wpabuf_free(req);
+}
+
+
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query_ap *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+ int dialog_token;
+
+ wpa_printf(MSG_DEBUG,
+ "GAS: No response to comeback request received (retry=%u)",
+ query->retry);
+ if (gas->current != query || query->retry)
+ return;
+ dialog_token = gas_query_new_dialog_token(gas, query->addr);
+ if (dialog_token < 0)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "GAS: Retry GAS query due to comeback response timeout");
+ query->retry = 1;
+ query->dialog_token = dialog_token;
+ *(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
+ query->wait_comeback = 0;
+ query->next_frag_id = 0;
+ wpabuf_free(query->adv_proto);
+ query->adv_proto = NULL;
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ gas_query_tx_initial_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query_ap *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ u16 comeback_delay)
+{
+ unsigned int secs, usecs;
+
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+ gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+ MACSTR " (dialog_token=%u comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+ if (query->adv_proto == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ query->wait_comeback = 1;
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ /* Query was completed without comeback mechanism */
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u8 frag_id, u8 more_frags,
+ u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+ "comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, frag_id,
+ more_frags, comeback_delay);
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
+ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+ os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+ wpabuf_len(query->adv_proto)) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+ "between initial and comeback response from "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ if (frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+ "with non-zero frag_id and comeback_delay "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+ return;
+ }
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ if (frag_id != query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+ "from " MACSTR, MAC2STR(query->addr));
+ if (frag_id + 1 == query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+ "retry of previous fragment");
+ return;
+ }
+ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+ return;
+ }
+ query->next_frag_id++;
+
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ if (more_frags) {
+ gas_query_tx_comeback_req(gas, query);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+/**
+ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
+ * frame
+ * @gas: GAS query data from gas_query_init()
+ * @sa: Source MAC address of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+ const u8 *data, size_t len, int freq)
+{
+ struct gas_query_pending *query;
+ u8 action, dialog_token, frag_id = 0, more_frags = 0;
+ u16 comeback_delay, resp_len;
+ const u8 *pos, *adv_proto;
+ int prot, pmf;
+ unsigned int left;
+
+ if (!gas || len < 4)
+ return -1;
+
+ pos = data;
+ action = *pos++;
+ dialog_token = *pos++;
+
+ if (action != WLAN_PA_GAS_INITIAL_RESP &&
+ action != WLAN_PA_GAS_COMEBACK_RESP)
+ return -1; /* Not a GAS response */
+
+ prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+ pmf = pmf_in_use(gas->hapd, sa);
+ if (prot && !pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+ return 0;
+ }
+ if (!prot && pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+ return 0;
+ }
+
+ query = gas_query_get_pending(gas, sa, dialog_token);
+ if (query == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+ " dialog token %u", MAC2STR(sa), dialog_token);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+ ms_from_time(&query->last_oper), MAC2STR(sa));
+
+ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+ MACSTR " dialog token %u when waiting for comeback "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+ MACSTR " dialog token %u when waiting for initial "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ query->status_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+ action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+ } else if (query->status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+ "%u failed - status code %u",
+ MAC2STR(sa), dialog_token, query->status_code);
+ gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
+ return 0;
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+ if (pos + 1 > data + len)
+ return 0;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ }
+
+ /* Comeback Delay */
+ if (pos + 2 > data + len)
+ return 0;
+ comeback_delay = WPA_GET_LE16(pos);
+ if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
+ comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
+ pos += 2;
+
+ /* Advertisement Protocol element */
+ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+ "Protocol element in the response from " MACSTR,
+ MAC2STR(sa));
+ return 0;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+ "Protocol element ID %u in response from " MACSTR,
+ *pos, MAC2STR(sa));
+ return 0;
+ }
+
+ adv_proto = pos;
+ pos += 2 + pos[1];
+
+ /* Query Response Length */
+ if (pos + 2 > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+ return 0;
+ }
+ resp_len = WPA_GET_LE16(pos);
+ pos += 2;
+
+ left = data + len - pos;
+ if (resp_len > left) {
+ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+ "response from " MACSTR, MAC2STR(sa));
+ return 0;
+ }
+
+ if (resp_len < left) {
+ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+ "after Query Response from " MACSTR,
+ left - resp_len, MAC2STR(sa));
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+ frag_id, more_frags, comeback_delay);
+ else
+ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+ comeback_delay);
+
+ return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query_ap *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+ " dialog token %u",
+ MAC2STR(query->addr), query->dialog_token);
+ gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query_ap *gas,
+ const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ dialog_token == q->dialog_token)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+ struct gas_query_pending *query)
+{
+ if (gas_query_tx(gas, query, query->req,
+ GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+ gas->current = query;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+ query->dialog_token);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+}
+
+
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
+{
+ static int next_start = 0;
+ int dialog_token;
+
+ for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+ if (gas_query_dialog_token_available(
+ gas, dst, (next_start + dialog_token) % 256))
+ break;
+ }
+ if (dialog_token == 256)
+ return -1; /* Too many pending queries */
+ dialog_token = (next_start + dialog_token) % 256;
+ next_start = (dialog_token + 1) % 256;
+ return dialog_token;
+}
+
+
+/**
+ * gas_query_ap_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ * return)
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx)
+{
+ struct gas_query_pending *query;
+ int dialog_token;
+
+ if (!gas || wpabuf_len(req) < 3)
+ return -1;
+
+ dialog_token = gas_query_new_dialog_token(gas, dst);
+ if (dialog_token < 0)
+ return -1;
+
+ query = os_zalloc(sizeof(*query));
+ if (query == NULL)
+ return -1;
+
+ query->gas = gas;
+ os_memcpy(query->addr, dst, ETH_ALEN);
+ query->dialog_token = dialog_token;
+ query->freq = freq;
+ query->cb = cb;
+ query->ctx = ctx;
+ query->req = req;
+ dl_list_add(&gas->pending, &query->list);
+
+ *(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+ " dialog_token=%u freq=%d",
+ MAC2STR(query->addr), query->dialog_token, query->freq);
+
+ gas_query_tx_initial_req(gas, query);
+
+ return dialog_token;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.h
new file mode 100644
index 0000000..70f1f05
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.h
@@ -0,0 +1,43 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_AP_H
+#define GAS_QUERY_AP_H
+
+struct gas_query_ap;
+
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+ void *msg_ctx);
+void gas_query_ap_deinit(struct gas_query_ap *gas);
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+ const u8 *data, size_t len, int freq);
+
+/**
+ * enum gas_query_ap_result - GAS query result
+ */
+enum gas_query_ap_result {
+ GAS_QUERY_AP_SUCCESS,
+ GAS_QUERY_AP_FAILURE,
+ GAS_QUERY_AP_TIMEOUT,
+ GAS_QUERY_AP_PEER_ERROR,
+ GAS_QUERY_AP_INTERNAL_ERROR,
+ GAS_QUERY_AP_DELETED_AT_DEINIT
+};
+
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx);
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+ const u8 *data, size_t data_len, int ok);
+
+#endif /* GAS_QUERY_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.c
new file mode 100644
index 0000000..4642e49
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.c
@@ -0,0 +1,1895 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "dpp_hostapd.h"
+#include "sta_info.h"
+#include "gas_serv.h"
+
+
+#ifdef CONFIG_DPP
+static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
+{
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 8); /* Length */
+ wpabuf_put_u8(buf, 0x7f);
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, DPP_OUI_TYPE);
+ wpabuf_put_u8(buf, 0x01);
+}
+#endif /* CONFIG_DPP */
+
+
+static void convert_to_protected_dual(struct wpabuf *msg)
+{
+ u8 *categ = wpabuf_mhead_u8(msg);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+}
+
+
+static struct gas_dialog_info *
+gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
+{
+ struct sta_info *sta;
+ struct gas_dialog_info *dia = NULL;
+ int i, j;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ /*
+ * We need a STA entry to be able to maintain state for
+ * the GAS query.
+ */
+ wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
+ "GAS query");
+ sta = ap_sta_add(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
+ " for GAS query", MAC2STR(addr));
+ return NULL;
+ }
+ sta->flags |= WLAN_STA_GAS;
+ /*
+ * The default inactivity is 300 seconds. We don't need
+ * it to be that long. Use five second timeout and increase this
+ * with the comeback_delay for testing cases.
+ */
+ ap_sta_session_timeout(hapd, sta,
+ hapd->conf->gas_comeback_delay / 1024 +
+ 5);
+ } else {
+ ap_sta_replenish_timeout(hapd, sta, 5);
+ }
+
+ if (sta->gas_dialog == NULL) {
+ sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
+ sizeof(struct gas_dialog_info));
+ if (sta->gas_dialog == NULL)
+ return NULL;
+ }
+
+ for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
+ if (i == GAS_DIALOG_MAX)
+ i = 0;
+ if (sta->gas_dialog[i].valid)
+ continue;
+ dia = &sta->gas_dialog[i];
+ dia->valid = 1;
+ dia->dialog_token = dialog_token;
+ sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
+ return dia;
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
+ MACSTR " dialog_token %u. Consider increasing "
+ "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
+
+ return NULL;
+}
+
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+ u8 dialog_token)
+{
+ struct sta_info *sta;
+ int i;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
+ MAC2STR(addr));
+ return NULL;
+ }
+ for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
+ if (sta->gas_dialog[i].dialog_token != dialog_token ||
+ !sta->gas_dialog[i].valid)
+ continue;
+ ap_sta_replenish_timeout(hapd, sta, 5);
+ return &sta->gas_dialog[i];
+ }
+ wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+ MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
+ return NULL;
+}
+
+
+void gas_serv_dialog_clear(struct gas_dialog_info *dia)
+{
+ wpabuf_free(dia->sd_resp);
+ os_memset(dia, 0, sizeof(*dia));
+}
+
+
+static void gas_serv_free_dialogs(struct hostapd_data *hapd,
+ const u8 *sta_addr)
+{
+ struct sta_info *sta;
+ int i;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (sta == NULL || sta->gas_dialog == NULL)
+ return;
+
+ for (i = 0; i < GAS_DIALOG_MAX; i++) {
+ if (sta->gas_dialog[i].valid)
+ return;
+ }
+
+ os_free(sta->gas_dialog);
+ sta->gas_dialog = NULL;
+}
+
+
+#ifdef CONFIG_HS20
+static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ u8 *len;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+ if (hapd->conf->hs20_oper_friendly_name)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ if (hapd->conf->hs20_wan_metrics)
+ wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+ if (hapd->conf->hs20_connection_capability)
+ wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+ if (hapd->conf->nai_realm_data)
+ wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+ if (hapd->conf->hs20_operating_class)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ if (hapd->conf->hs20_osu_providers_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ if (hapd->conf->hs20_osu_providers_nai_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+ if (hapd->conf->hs20_icons_count)
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+ if (hapd->conf->hs20_operator_icon_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+ gas_anqp_set_element_len(buf, len);
+}
+#endif /* CONFIG_HS20 */
+
+
+static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
+ u16 infoid)
+{
+ struct anqp_element *elem;
+
+ dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
+ list) {
+ if (elem->infoid == infoid)
+ return elem;
+ }
+
+ return NULL;
+}
+
+
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+ u16 infoid)
+{
+ struct anqp_element *elem;
+
+ elem = get_anqp_elem(hapd, infoid);
+ if (!elem)
+ return;
+ if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
+ wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
+ infoid);
+ return;
+ }
+
+ wpabuf_put_le16(buf, infoid);
+ wpabuf_put_le16(buf, wpabuf_len(elem->payload));
+ wpabuf_put_buf(buf, elem->payload);
+}
+
+
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+ u16 infoid)
+{
+ if (get_anqp_elem(hapd, infoid)) {
+ anqp_add_elem(hapd, buf, infoid);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void anqp_add_capab_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ u8 *len;
+ u16 id;
+
+ if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+ return;
+
+ len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+ wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+ if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
+ wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+ if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
+ wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
+ if (hapd->conf->network_auth_type ||
+ get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
+ wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+ if (hapd->conf->roaming_consortium ||
+ get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
+ wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+ if (hapd->conf->ipaddr_type_configured ||
+ get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+ wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+ if (hapd->conf->nai_realm_data ||
+ get_anqp_elem(hapd, ANQP_NAI_REALM))
+ wpabuf_put_le16(buf, ANQP_NAI_REALM);
+ if (hapd->conf->anqp_3gpp_cell_net ||
+ get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
+ wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+ if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
+ wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
+ if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
+ wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
+ if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
+ wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
+ if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
+ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+ if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
+ wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+ if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
+ wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
+ if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
+ wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
+ if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
+ wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
+#ifdef CONFIG_FILS
+ if (!dl_list_empty(&hapd->conf->fils_realms) ||
+ get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
+ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+#endif /* CONFIG_FILS */
+ if (get_anqp_elem(hapd, ANQP_CAG))
+ wpabuf_put_le16(buf, ANQP_CAG);
+ if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
+ wpabuf_put_le16(buf, ANQP_VENUE_URL);
+ if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
+ wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
+ if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
+ wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
+ for (id = 280; id < 300; id++) {
+ if (get_anqp_elem(hapd, id))
+ wpabuf_put_le16(buf, id);
+ }
+#ifdef CONFIG_HS20
+ anqp_add_hs_capab_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+ gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
+ return;
+
+ if (hapd->conf->venue_name) {
+ u8 *len;
+ unsigned int i;
+ len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
+ wpabuf_put_u8(buf, hapd->conf->venue_group);
+ wpabuf_put_u8(buf, hapd->conf->venue_type);
+ for (i = 0; i < hapd->conf->venue_name_count; i++) {
+ struct hostapd_lang_string *vn;
+ vn = &hapd->conf->venue_name[i];
+ wpabuf_put_u8(buf, 3 + vn->name_len);
+ wpabuf_put_data(buf, vn->lang, 3);
+ wpabuf_put_data(buf, vn->name, vn->name_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
+ return;
+
+ if (hapd->conf->venue_url) {
+ u8 *len;
+ unsigned int i;
+
+ len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
+ for (i = 0; i < hapd->conf->venue_url_count; i++) {
+ struct hostapd_venue_url *url;
+
+ url = &hapd->conf->venue_url[i];
+ wpabuf_put_u8(buf, 1 + url->url_len);
+ wpabuf_put_u8(buf, url->venue_number);
+ wpabuf_put_data(buf, url->url, url->url_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
+ return;
+
+ if (hapd->conf->network_auth_type) {
+ wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+ wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
+ wpabuf_put_data(buf, hapd->conf->network_auth_type,
+ hapd->conf->network_auth_type_len);
+ }
+}
+
+
+static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ unsigned int i;
+ u8 *len;
+
+ if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
+ return;
+
+ len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+ for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+ struct hostapd_roaming_consortium *rc;
+ rc = &hapd->conf->roaming_consortium[i];
+ wpabuf_put_u8(buf, rc->len);
+ wpabuf_put_data(buf, rc->oi, rc->len);
+ }
+ gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+ return;
+
+ if (hapd->conf->ipaddr_type_configured) {
+ wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
+ }
+}
+
+
+static void anqp_add_nai_realm_eap(struct wpabuf *buf,
+ struct hostapd_nai_realm_data *realm)
+{
+ unsigned int i, j;
+
+ wpabuf_put_u8(buf, realm->eap_method_count);
+
+ for (i = 0; i < realm->eap_method_count; i++) {
+ struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+ wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
+ wpabuf_put_u8(buf, eap->eap_method);
+ wpabuf_put_u8(buf, eap->num_auths);
+ for (j = 0; j < eap->num_auths; j++) {
+ wpabuf_put_u8(buf, eap->auth_id[j]);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, eap->auth_val[j]);
+ }
+ }
+}
+
+
+static void anqp_add_nai_realm_data(struct wpabuf *buf,
+ struct hostapd_nai_realm_data *realm,
+ unsigned int realm_idx)
+{
+ u8 *realm_data_len;
+
+ wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
+ (int) os_strlen(realm->realm[realm_idx]));
+ realm_data_len = wpabuf_put(buf, 2);
+ wpabuf_put_u8(buf, realm->encoding);
+ wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
+ wpabuf_put_str(buf, realm->realm[realm_idx]);
+ anqp_add_nai_realm_eap(buf, realm);
+ gas_anqp_set_element_len(buf, realm_data_len);
+}
+
+
+static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *home_realm,
+ size_t home_realm_len)
+{
+ unsigned int i, j, k;
+ u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
+ struct hostapd_nai_realm_data *realm;
+ const u8 *pos, *realm_name, *end;
+ struct {
+ unsigned int realm_data_idx;
+ unsigned int realm_idx;
+ } matches[10];
+
+ pos = home_realm;
+ end = pos + home_realm_len;
+ if (end - pos < 1) {
+ wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+ }
+ num_realms = *pos++;
+
+ for (i = 0; i < num_realms && num_matching < 10; i++) {
+ if (end - pos < 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "Truncated NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+ }
+ encoding = *pos++;
+ realm_len = *pos++;
+ if (realm_len > end - pos) {
+ wpa_hexdump(MSG_DEBUG,
+ "Truncated NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+ }
+ realm_name = pos;
+ for (j = 0; j < hapd->conf->nai_realm_count &&
+ num_matching < 10; j++) {
+ const u8 *rpos, *rend;
+ realm = &hapd->conf->nai_realm_data[j];
+ if (encoding != realm->encoding)
+ continue;
+
+ rpos = realm_name;
+ while (rpos < realm_name + realm_len &&
+ num_matching < 10) {
+ for (rend = rpos;
+ rend < realm_name + realm_len; rend++) {
+ if (*rend == ';')
+ break;
+ }
+ for (k = 0; k < MAX_NAI_REALMS &&
+ realm->realm[k] &&
+ num_matching < 10; k++) {
+ if ((int) os_strlen(realm->realm[k]) !=
+ rend - rpos ||
+ os_strncmp((char *) rpos,
+ realm->realm[k],
+ rend - rpos) != 0)
+ continue;
+ matches[num_matching].realm_data_idx =
+ j;
+ matches[num_matching].realm_idx = k;
+ num_matching++;
+ }
+ rpos = rend + 1;
+ }
+ }
+ pos += realm_len;
+ }
+
+ realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+ wpabuf_put_le16(buf, num_matching);
+
+ /*
+ * There are two ways to format. 1. each realm in a NAI Realm Data unit
+ * 2. all realms that share the same EAP methods in a NAI Realm Data
+ * unit. The first format is likely to be bigger in size than the
+ * second, but may be easier to parse and process by the receiver.
+ */
+ for (i = 0; i < num_matching; i++) {
+ wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
+ matches[i].realm_data_idx, matches[i].realm_idx);
+ realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
+ anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
+ }
+ gas_anqp_set_element_len(buf, realm_list_len);
+ return 0;
+}
+
+
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
+ const u8 *home_realm, size_t home_realm_len,
+ int nai_realm, int nai_home_realm)
+{
+ if (nai_realm && !nai_home_realm &&
+ anqp_add_override(hapd, buf, ANQP_NAI_REALM))
+ return;
+
+ if (nai_realm && hapd->conf->nai_realm_data) {
+ u8 *len;
+ unsigned int i, j;
+ len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+ wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+ for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+ u8 *realm_data_len, *realm_len;
+ struct hostapd_nai_realm_data *realm;
+
+ realm = &hapd->conf->nai_realm_data[i];
+ realm_data_len = wpabuf_put(buf, 2);
+ wpabuf_put_u8(buf, realm->encoding);
+ realm_len = wpabuf_put(buf, 1);
+ for (j = 0; realm->realm[j]; j++) {
+ if (j > 0)
+ wpabuf_put_u8(buf, ';');
+ wpabuf_put_str(buf, realm->realm[j]);
+ }
+ *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
+ anqp_add_nai_realm_eap(buf, realm);
+ gas_anqp_set_element_len(buf, realm_data_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
+ hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
+ home_realm_len);
+ }
+}
+
+
+static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
+ return;
+
+ if (hapd->conf->anqp_3gpp_cell_net) {
+ wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+ wpabuf_put_le16(buf,
+ hapd->conf->anqp_3gpp_cell_net_len);
+ wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
+ hapd->conf->anqp_3gpp_cell_net_len);
+ }
+}
+
+
+static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
+ return;
+
+ if (hapd->conf->domain_name) {
+ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+ wpabuf_put_le16(buf, hapd->conf->domain_name_len);
+ wpabuf_put_data(buf, hapd->conf->domain_name,
+ hapd->conf->domain_name_len);
+ }
+}
+
+
+#ifdef CONFIG_FILS
+static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ size_t count;
+
+ if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
+ return;
+
+ count = dl_list_len(&hapd->conf->fils_realms);
+ if (count > 10000)
+ count = 10000;
+ if (count) {
+ struct fils_realm *realm;
+
+ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+ wpabuf_put_le16(buf, 2 * count);
+
+ dl_list_for_each(realm, &hapd->conf->fils_realms,
+ struct fils_realm, list) {
+ if (count == 0)
+ break;
+ wpabuf_put_data(buf, realm->hash, 2);
+ count--;
+ }
+ }
+}
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_HS20
+
+static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_oper_friendly_name) {
+ u8 *len;
+ unsigned int i;
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
+ {
+ struct hostapd_lang_string *vn;
+ vn = &hapd->conf->hs20_oper_friendly_name[i];
+ wpabuf_put_u8(buf, 3 + vn->name_len);
+ wpabuf_put_data(buf, vn->lang, 3);
+ wpabuf_put_data(buf, vn->name, vn->name_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_wan_metrics(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_wan_metrics) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_connection_capability(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_connection_capability) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
+ hapd->conf->hs20_connection_capability_len);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_operating_class(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_operating_class) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
+ hapd->conf->hs20_operating_class_len);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
+ const char *name)
+{
+ size_t j;
+ struct hs20_icon *icon = NULL;
+
+ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+ if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
+ icon = &bss->hs20_icons[j];
+ }
+ if (!icon)
+ return; /* icon info not found */
+
+ wpabuf_put_le16(buf, icon->width);
+ wpabuf_put_le16(buf, icon->height);
+ wpabuf_put_data(buf, icon->language, 3);
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_u8(buf, os_strlen(icon->name));
+ wpabuf_put_str(buf, icon->name);
+}
+
+
+static void anqp_add_osu_provider(struct wpabuf *buf,
+ struct hostapd_bss_config *bss,
+ struct hs20_osu_provider *p)
+{
+ u8 *len, *len2, *count;
+ unsigned int i;
+
+ len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+ /* OSU Friendly Name Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->friendly_name_count; i++) {
+ struct hostapd_lang_string *s = &p->friendly_name[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU Server URI */
+ if (p->server_uri) {
+ wpabuf_put_u8(buf, os_strlen(p->server_uri));
+ wpabuf_put_str(buf, p->server_uri);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Method List */
+ count = wpabuf_put(buf, 1);
+ for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
+ wpabuf_put_u8(buf, p->method_list[i]);
+ *count = i;
+
+ /* Icons Available */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->icons_count; i++)
+ anqp_add_icon(buf, bss, p->icons[i]);
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU_NAI */
+ if (p->osu_nai) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+ wpabuf_put_str(buf, p->osu_nai);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Service Description Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->service_desc_count; i++) {
+ struct hostapd_lang_string *s = &p->service_desc[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_count) {
+ size_t i;
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ /* OSU SSID */
+ wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+ wpabuf_put_data(buf, hapd->conf->osu_ssid,
+ hapd->conf->osu_ssid_len);
+
+ /* Number of OSU Providers */
+ wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider(
+ buf, hapd->conf,
+ &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_osu_provider_nai(struct wpabuf *buf,
+ struct hs20_osu_provider *p)
+{
+ /* OSU_NAI for shared BSS (Single SSID) */
+ if (p->osu_nai2) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
+ wpabuf_put_str(buf, p->osu_nai2);
+ } else {
+ wpabuf_put_u8(buf, 0);
+ }
+}
+
+
+static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_nai_count) {
+ size_t i;
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider_nai(
+ buf, &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *name, size_t name_len)
+{
+ struct hs20_icon *icon;
+ size_t i;
+ u8 *len;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+ name, name_len);
+ for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+ icon = &hapd->conf->hs20_icons[i];
+ if (name_len == os_strlen(icon->name) &&
+ os_memcmp(name, icon->name, name_len) == 0)
+ break;
+ }
+
+ if (i < hapd->conf->hs20_icons_count)
+ icon = &hapd->conf->hs20_icons[i];
+ else
+ icon = NULL;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ if (icon) {
+ char *data;
+ size_t data_len;
+
+ data = os_readfile(icon->file, &data_len);
+ if (data == NULL || data_len > 65535) {
+ wpabuf_put_u8(buf, 2); /* Download Status:
+ * Unspecified file error */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ } else {
+ wpabuf_put_u8(buf, 0); /* Download Status: Success */
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_le16(buf, data_len);
+ wpabuf_put_data(buf, data, data_len);
+ }
+ os_free(data);
+ } else {
+ wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ struct hostapd_bss_config *bss = hapd->conf;
+ size_t i;
+ u8 *len;
+
+ if (!bss->hs20_operator_icon_count)
+ return;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ for (i = 0; i < bss->hs20_operator_icon_count; i++)
+ anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
+
+ gas_anqp_set_element_len(buf, len);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_MBO
+static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+ wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+#endif /* CONFIG_MBO */
+
+
+static size_t anqp_get_required_len(struct hostapd_data *hapd,
+ const u16 *infoid,
+ unsigned int num_infoid)
+{
+ size_t len = 0;
+ unsigned int i;
+
+ for (i = 0; i < num_infoid; i++) {
+ struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
+
+ if (elem)
+ len += 2 + 2 + wpabuf_len(elem->payload);
+ }
+
+ return len;
+}
+
+
+static struct wpabuf *
+gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ unsigned int request,
+ const u8 *home_realm, size_t home_realm_len,
+ const u8 *icon_name, size_t icon_name_len,
+ const u16 *extra_req,
+ unsigned int num_extra_req)
+{
+ struct wpabuf *buf;
+ size_t len;
+ unsigned int i;
+
+ len = 1400;
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+ len += 1000;
+ if (request & ANQP_REQ_ICON_REQUEST)
+ len += 65536;
+#ifdef CONFIG_FILS
+ if (request & ANQP_FILS_REALM_INFO)
+ len += 2 * dl_list_len(&hapd->conf->fils_realms);
+#endif /* CONFIG_FILS */
+ len += anqp_get_required_len(hapd, extra_req, num_extra_req);
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ if (request & ANQP_REQ_CAPABILITY_LIST)
+ anqp_add_capab_list(hapd, buf);
+ if (request & ANQP_REQ_VENUE_NAME)
+ anqp_add_venue_name(hapd, buf);
+ if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
+ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
+ if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
+ anqp_add_network_auth_type(hapd, buf);
+ if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+ anqp_add_roaming_consortium(hapd, buf);
+ if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
+ anqp_add_ip_addr_type_availability(hapd, buf);
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+ anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
+ request & ANQP_REQ_NAI_REALM,
+ request & ANQP_REQ_NAI_HOME_REALM);
+ if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
+ anqp_add_3gpp_cellular_network(hapd, buf);
+ if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
+ anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
+ if (request & ANQP_REQ_AP_CIVIC_LOCATION)
+ anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
+ if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
+ anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
+ if (request & ANQP_REQ_DOMAIN_NAME)
+ anqp_add_domain_name(hapd, buf);
+ if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
+ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
+ if (request & ANQP_REQ_TDLS_CAPABILITY)
+ anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+ if (request & ANQP_REQ_EMERGENCY_NAI)
+ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
+ for (i = 0; i < num_extra_req; i++) {
+#ifdef CONFIG_FILS
+ if (extra_req[i] == ANQP_FILS_REALM_INFO) {
+ anqp_add_fils_realm_info(hapd, buf);
+ continue;
+ }
+#endif /* CONFIG_FILS */
+ if (extra_req[i] == ANQP_VENUE_URL) {
+ anqp_add_venue_url(hapd, buf);
+ continue;
+ }
+ anqp_add_elem(hapd, buf, extra_req[i]);
+ }
+
+#ifdef CONFIG_HS20
+ if (request & ANQP_REQ_HS_CAPABILITY_LIST)
+ anqp_add_hs_capab_list(hapd, buf);
+ if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
+ anqp_add_operator_friendly_name(hapd, buf);
+ if (request & ANQP_REQ_WAN_METRICS)
+ anqp_add_wan_metrics(hapd, buf);
+ if (request & ANQP_REQ_CONNECTION_CAPABILITY)
+ anqp_add_connection_capability(hapd, buf);
+ if (request & ANQP_REQ_OPERATING_CLASS)
+ anqp_add_operating_class(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+ anqp_add_osu_providers_list(hapd, buf);
+ if (request & ANQP_REQ_ICON_REQUEST)
+ anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
+ if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
+ anqp_add_operator_icon_metadata(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
+ anqp_add_osu_providers_nai_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MBO
+ if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
+ anqp_add_mbo_cell_data_conn_pref(hapd, buf);
+#endif /* CONFIG_MBO */
+
+ return buf;
+}
+
+
+#define ANQP_MAX_EXTRA_REQ 20
+
+struct anqp_query_info {
+ unsigned int request;
+ const u8 *home_realm_query;
+ size_t home_realm_query_len;
+ const u8 *icon_name;
+ size_t icon_name_len;
+ int p2p_sd;
+ u16 extra_req[ANQP_MAX_EXTRA_REQ];
+ unsigned int num_extra_req;
+};
+
+
+static void set_anqp_req(unsigned int bit, const char *name, int local,
+ struct anqp_query_info *qi)
+{
+ qi->request |= bit;
+ if (local) {
+ wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
+ }
+}
+
+
+static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+ struct anqp_query_info *qi)
+{
+ switch (info_id) {
+ case ANQP_CAPABILITY_LIST:
+ set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
+ qi);
+ break;
+ case ANQP_VENUE_NAME:
+ set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
+ hapd->conf->venue_name != NULL, qi);
+ break;
+ case ANQP_EMERGENCY_CALL_NUMBER:
+ set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
+ "Emergency Call Number",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_NETWORK_AUTH_TYPE:
+ set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
+ hapd->conf->network_auth_type != NULL, qi);
+ break;
+ case ANQP_ROAMING_CONSORTIUM:
+ set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
+ hapd->conf->roaming_consortium != NULL, qi);
+ break;
+ case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+ set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
+ "IP Addr Type Availability",
+ hapd->conf->ipaddr_type_configured, qi);
+ break;
+ case ANQP_NAI_REALM:
+ set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
+ hapd->conf->nai_realm_data != NULL, qi);
+ break;
+ case ANQP_3GPP_CELLULAR_NETWORK:
+ set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
+ "3GPP Cellular Network",
+ hapd->conf->anqp_3gpp_cell_net != NULL, qi);
+ break;
+ case ANQP_AP_GEOSPATIAL_LOCATION:
+ set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
+ "AP Geospatial Location",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_AP_CIVIC_LOCATION:
+ set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
+ "AP Civic Location",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_AP_LOCATION_PUBLIC_URI:
+ set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
+ "AP Location Public URI",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_DOMAIN_NAME:
+ set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
+ hapd->conf->domain_name != NULL, qi);
+ break;
+ case ANQP_EMERGENCY_ALERT_URI:
+ set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
+ "Emergency Alert URI",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_TDLS_CAPABILITY:
+ set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
+ "TDLS Capability",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_EMERGENCY_NAI:
+ set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
+ "Emergency NAI",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ default:
+#ifdef CONFIG_FILS
+ if (info_id == ANQP_FILS_REALM_INFO &&
+ !dl_list_empty(&hapd->conf->fils_realms)) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: FILS Realm Information (local)");
+ } else
+#endif /* CONFIG_FILS */
+ if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Venue URL (local)");
+ } else if (!get_anqp_elem(hapd, info_id)) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+ info_id);
+ break;
+ }
+ if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: No more room for extra requests - ignore Info Id %u",
+ info_id);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
+ qi->extra_req[qi->num_extra_req] = info_id;
+ qi->num_extra_req++;
+ break;
+ }
+}
+
+
+static void rx_anqp_query_list(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+ (unsigned int) (end - pos) / 2);
+
+ while (end - pos >= 2) {
+ rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+ pos += 2;
+ }
+}
+
+
+#ifdef CONFIG_HS20
+
+static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
+ struct anqp_query_info *qi)
+{
+ switch (subtype) {
+ case HS20_STYPE_CAPABILITY_LIST:
+ set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
+ 1, qi);
+ break;
+ case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+ set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
+ "Operator Friendly Name",
+ hapd->conf->hs20_oper_friendly_name != NULL, qi);
+ break;
+ case HS20_STYPE_WAN_METRICS:
+ set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
+ hapd->conf->hs20_wan_metrics != NULL, qi);
+ break;
+ case HS20_STYPE_CONNECTION_CAPABILITY:
+ set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
+ "Connection Capability",
+ hapd->conf->hs20_connection_capability != NULL,
+ qi);
+ break;
+ case HS20_STYPE_OPERATING_CLASS:
+ set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
+ hapd->conf->hs20_operating_class != NULL, qi);
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+ hapd->conf->hs20_osu_providers_count, qi);
+ break;
+ case HS20_STYPE_OPERATOR_ICON_METADATA:
+ set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
+ "Operator Icon Metadata",
+ hapd->conf->hs20_operator_icon_count, qi);
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
+ "OSU Providers NAI List",
+ hapd->conf->hs20_osu_providers_nai_count, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
+ subtype);
+ break;
+ }
+}
+
+
+static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ qi->request |= ANQP_REQ_NAI_HOME_REALM;
+ qi->home_realm_query = pos;
+ qi->home_realm_query_len = end - pos;
+ if (hapd->conf->nai_realm_data != NULL) {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
+ "(local)");
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
+ "available");
+ }
+}
+
+
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ qi->request |= ANQP_REQ_ICON_REQUEST;
+ qi->icon_name = pos;
+ qi->icon_name_len = end - pos;
+ if (hapd->conf->hs20_icons_count) {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+ "(local)");
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+ "available");
+ }
+}
+
+
+static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ u8 subtype;
+
+ if (end - pos <= 1)
+ return;
+
+ subtype = *pos++;
+ pos++; /* Reserved */
+ switch (subtype) {
+ case HS20_STYPE_QUERY_LIST:
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
+ while (pos < end) {
+ rx_anqp_hs_query_list(hapd, *pos, qi);
+ pos++;
+ }
+ break;
+ case HS20_STYPE_NAI_HOME_REALM_QUERY:
+ rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
+ break;
+ case HS20_STYPE_ICON_REQUEST:
+ rx_anqp_hs_icon_request(hapd, pos, end, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
+ "%u", subtype);
+ break;
+ }
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_P2P
+static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
+ struct anqp_query_info *qi)
+{
+ /*
+ * This is for P2P SD and will be taken care of by the P2P
+ * implementation. This query needs to be ignored in the generic
+ * GAS server to avoid duplicated response.
+ */
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+ P2P_OUI_TYPE);
+ qi->p2p_sd = 1;
+ return;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MBO
+
+static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
+ struct anqp_query_info *qi)
+{
+ switch (subtype) {
+ case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+ set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
+ "Cellular Data Connection Preference",
+ hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
+ subtype);
+ break;
+ }
+}
+
+
+static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ u8 subtype;
+
+ if (end - pos < 1)
+ return;
+
+ subtype = *pos++;
+ switch (subtype) {
+ case MBO_ANQP_SUBTYPE_QUERY_LIST:
+ wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
+ while (pos < end) {
+ rx_anqp_mbo_query_list(hapd, *pos, qi);
+ pos++;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
+ subtype);
+ break;
+ }
+}
+
+#endif /* CONFIG_MBO */
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ u32 oui;
+
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+ "Query element");
+ return;
+ }
+
+ oui = WPA_GET_BE24(pos);
+ pos += 3;
+ if (oui != OUI_WFA) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+ oui);
+ return;
+ }
+
+ switch (*pos) {
+#ifdef CONFIG_P2P
+ case P2P_OUI_TYPE:
+ rx_anqp_vendor_specific_p2p(hapd, qi);
+ break;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+ case HS20_ANQP_OUI_TYPE:
+ rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
+ break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ case MBO_ANQP_OUI_TYPE:
+ rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
+ break;
+#endif /* CONFIG_MBO */
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+ *pos);
+ break;
+ }
+}
+
+
+static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+ const u8 *sa, u8 dialog_token,
+ struct anqp_query_info *qi, int prot,
+ int std_addr3)
+{
+ struct wpabuf *buf, *tx_buf;
+
+ buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
+ qi->home_realm_query,
+ qi->home_realm_query_len,
+ qi->icon_name, qi->icon_name_len,
+ qi->extra_req, qi->num_extra_req);
+ wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+ buf);
+ if (!buf)
+ return;
+#ifdef CONFIG_P2P
+ if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
+ wpabuf_free(buf);
+ return;
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+ hapd->conf->gas_comeback_delay) {
+ struct gas_dialog_info *di;
+ u16 comeback_delay = 1;
+
+ if (hapd->conf->gas_comeback_delay) {
+ /* Testing - allow overriding of the delay value */
+ comeback_delay = hapd->conf->gas_comeback_delay;
+ }
+
+ wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
+ "initial response - use GAS comeback");
+ di = gas_dialog_create(hapd, sa, dialog_token);
+ if (!di) {
+ wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
+ "for " MACSTR " (dialog token %u)",
+ MAC2STR(sa), dialog_token);
+ wpabuf_free(buf);
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ 0, NULL);
+ } else {
+ di->prot = prot;
+ di->sd_resp = buf;
+ di->sd_resp_pos = 0;
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_SUCCESS,
+ comeback_delay, NULL);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
+ wpabuf_free(buf);
+ }
+ if (!tx_buf)
+ return;
+ if (prot)
+ convert_to_protected_dual(tx_buf);
+ if (std_addr3)
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ else
+ hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+}
+
+
+#ifdef CONFIG_DPP
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+ const u8 *sa, u8 dialog_token,
+ int prot, struct wpabuf *buf, int freq)
+{
+ struct wpabuf *tx_buf;
+
+ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+ hapd->conf->gas_comeback_delay) {
+ struct gas_dialog_info *di;
+ u16 comeback_delay = 1;
+
+ if (hapd->conf->gas_comeback_delay) {
+ /* Testing - allow overriding of the delay value */
+ comeback_delay = hapd->conf->gas_comeback_delay;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too long response to fit in initial response - use GAS comeback");
+ di = gas_dialog_create(hapd, sa, dialog_token);
+ if (!di) {
+ wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
+ MACSTR " (dialog token %u)",
+ MAC2STR(sa), dialog_token);
+ wpabuf_free(buf);
+ tx_buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ 0, 10);
+ if (tx_buf)
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ } else {
+ di->prot = prot;
+ di->sd_resp = buf;
+ di->sd_resp_pos = 0;
+ di->dpp = 1;
+ tx_buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_SUCCESS,
+ comeback_delay, 10 + 2);
+ if (tx_buf) {
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_le16(tx_buf, 0);
+ }
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: GAS Initial response (no comeback)");
+ tx_buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_SUCCESS, 0,
+ 10 + 2 + wpabuf_len(buf));
+ if (tx_buf) {
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_le16(tx_buf, wpabuf_len(buf));
+ wpabuf_put_buf(tx_buf, buf);
+ hostapd_dpp_gas_status_handler(hapd, 1);
+ }
+ wpabuf_free(buf);
+ }
+ if (!tx_buf)
+ return;
+ if (prot)
+ convert_to_protected_dual(tx_buf);
+ hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+}
+#endif /* CONFIG_DPP */
+
+
+static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+ const u8 *sa,
+ const u8 *data, size_t len, int prot,
+ int std_addr3, int freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 slen;
+ struct anqp_query_info qi;
+ const u8 *adv_proto;
+#ifdef CONFIG_DPP
+ int dpp = 0;
+#endif /* CONFIG_DPP */
+
+ if (len < 1 + 2)
+ return;
+
+ os_memset(&qi, 0, sizeof(qi));
+
+ dialog_token = *pos++;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
+ MAC2STR(sa), dialog_token);
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
+ return;
+ }
+ adv_proto = pos++;
+
+ slen = *pos++;
+ if (slen > end - pos || slen < 2) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Invalid IE in GAS Initial Request");
+ return;
+ }
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+#ifdef CONFIG_DPP
+ if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
+ pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
+ pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
+ dpp = 1;
+ } else
+#endif /* CONFIG_DPP */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ struct wpabuf *buf;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ if (sa[0] & 0x01)
+ return; /* Invalid source address - drop silently */
+ buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
+ 0, 2 + slen + 2);
+ if (buf == NULL)
+ return;
+ wpabuf_put_data(buf, adv_proto, 2 + slen);
+ wpabuf_put_le16(buf, 0); /* Query Response Length */
+ if (prot)
+ convert_to_protected_dual(buf);
+ if (std_addr3)
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(buf),
+ wpabuf_len(buf));
+ else
+ hostapd_drv_send_action_addr3_ap(hapd,
+ hapd->iface->freq, 0,
+ sa, wpabuf_head(buf),
+ wpabuf_len(buf));
+ wpabuf_free(buf);
+ return;
+ }
+
+ pos = next;
+ /* Query Request */
+ if (end - pos < 2)
+ return;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return;
+ end = pos + slen;
+
+#ifdef CONFIG_DPP
+ if (dpp) {
+ struct wpabuf *msg;
+
+ msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
+ data, len);
+ if (!msg)
+ return;
+ gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
+ freq);
+ return;
+ }
+#endif /* CONFIG_DPP */
+
+ /* ANQP Query Request */
+ while (pos < end) {
+ u16 info_id, elen;
+
+ if (end - pos < 4)
+ return;
+
+ info_id = WPA_GET_LE16(pos);
+ pos += 2;
+ elen = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (elen > end - pos) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+ return;
+ }
+
+ switch (info_id) {
+ case ANQP_QUERY_LIST:
+ rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+ break;
+ case ANQP_VENDOR_SPECIFIC:
+ rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+ "Request element %u", info_id);
+ break;
+ }
+
+ pos += elen;
+ }
+
+ gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
+ std_addr3);
+}
+
+
+static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
+ const u8 *sa,
+ const u8 *data, size_t len, int prot,
+ int std_addr3)
+{
+ struct gas_dialog_info *dialog;
+ struct wpabuf *buf, *tx_buf;
+ u8 dialog_token;
+ size_t frag_len;
+ int more = 0;
+
+ wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
+ if (len < 1)
+ return;
+ dialog_token = *data;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
+ dialog_token);
+
+ dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
+ if (!dialog) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
+ "response fragment for " MACSTR " dialog token %u",
+ MAC2STR(sa), dialog_token);
+
+ if (sa[0] & 0x01)
+ return; /* Invalid source address - drop silently */
+ tx_buf = gas_anqp_build_comeback_resp_buf(
+ dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
+ 0, NULL);
+ if (tx_buf == NULL)
+ return;
+ goto send_resp;
+ }
+
+ frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+ if (frag_len > hapd->conf->gas_frag_limit) {
+ frag_len = hapd->conf->gas_frag_limit;
+ more = 1;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+ (unsigned int) frag_len);
+ buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+ dialog->sd_resp_pos, frag_len);
+ if (buf == NULL) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
+ "buffer");
+ gas_serv_dialog_clear(dialog);
+ return;
+ }
+#ifdef CONFIG_DPP
+ if (dialog->dpp) {
+ tx_buf = gas_build_comeback_resp(dialog_token,
+ WLAN_STATUS_SUCCESS,
+ dialog->sd_frag_id, more, 0,
+ 10 + 2 + frag_len);
+ if (tx_buf) {
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_le16(tx_buf, frag_len);
+ wpabuf_put_buf(tx_buf, buf);
+ }
+ } else
+#endif /* CONFIG_DPP */
+ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
+ WLAN_STATUS_SUCCESS,
+ dialog->sd_frag_id,
+ more, 0, buf);
+ wpabuf_free(buf);
+ if (tx_buf == NULL) {
+ gas_serv_dialog_clear(dialog);
+ return;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
+ "(frag_id %d more=%d frag_len=%d)",
+ dialog->sd_frag_id, more, (int) frag_len);
+ dialog->sd_frag_id++;
+ dialog->sd_resp_pos += frag_len;
+
+ if (more) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
+ "to be sent",
+ (int) (wpabuf_len(dialog->sd_resp) -
+ dialog->sd_resp_pos));
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+ "SD response sent");
+#ifdef CONFIG_DPP
+ if (dialog->dpp)
+ hostapd_dpp_gas_status_handler(hapd, 1);
+#endif /* CONFIG_DPP */
+ gas_serv_dialog_clear(dialog);
+ gas_serv_free_dialogs(hapd, sa);
+ }
+
+send_resp:
+ if (prot)
+ convert_to_protected_dual(tx_buf);
+ if (std_addr3)
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ else
+ hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+}
+
+
+static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
+ int freq)
+{
+ struct hostapd_data *hapd = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *sa, *data;
+ int prot, std_addr3;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ if (len < IEEE80211_HDRLEN + 2)
+ return;
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
+ return;
+ /*
+ * Note: Public Action and Protected Dual of Public Action frames share
+ * the same payload structure, so it is fine to use definitions of
+ * Public Action frames to process both.
+ */
+ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
+ sa = mgmt->sa;
+ if (hapd->conf->gas_address3 == 1)
+ std_addr3 = 1;
+ else if (hapd->conf->gas_address3 == 2)
+ std_addr3 = 0;
+ else
+ std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
+ len -= IEEE80211_HDRLEN + 1;
+ data = buf + IEEE80211_HDRLEN + 1;
+ switch (data[0]) {
+ case WLAN_PA_GAS_INITIAL_REQ:
+ gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
+ std_addr3, freq);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+ gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
+ std_addr3);
+ break;
+ }
+}
+
+
+int gas_serv_init(struct hostapd_data *hapd)
+{
+ hapd->public_action_cb2 = gas_serv_rx_public_action;
+ hapd->public_action_cb2_ctx = hapd;
+ return 0;
+}
+
+
+void gas_serv_deinit(struct hostapd_data *hapd)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.h
new file mode 100644
index 0000000..7646a98
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.h
@@ -0,0 +1,95 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERV_H
+#define GAS_SERV_H
+
+/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
+#define ANQP_REQ_CAPABILITY_LIST \
+ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+#define ANQP_REQ_VENUE_NAME \
+ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
+ (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
+#define ANQP_REQ_NETWORK_AUTH_TYPE \
+ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
+#define ANQP_REQ_ROAMING_CONSORTIUM \
+ (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
+#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
+ (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_NAI_REALM \
+ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
+#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
+ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
+ (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_CIVIC_LOCATION \
+ (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
+ (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_DOMAIN_NAME \
+ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_ALERT_URI \
+ (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_TDLS_CAPABILITY \
+ (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_NAI \
+ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
+/*
+ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * optimized bitmap.
+ */
+#define ANQP_REQ_HS_CAPABILITY_LIST \
+ (0x10000 << HS20_STYPE_CAPABILITY_LIST)
+#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
+ (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
+#define ANQP_REQ_WAN_METRICS \
+ (0x10000 << HS20_STYPE_WAN_METRICS)
+#define ANQP_REQ_CONNECTION_CAPABILITY \
+ (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
+#define ANQP_REQ_NAI_HOME_REALM \
+ (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
+#define ANQP_REQ_OPERATING_CLASS \
+ (0x10000 << HS20_STYPE_OPERATING_CLASS)
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+#define ANQP_REQ_ICON_REQUEST \
+ (0x10000 << HS20_STYPE_ICON_REQUEST)
+#define ANQP_REQ_OPERATOR_ICON_METADATA \
+ (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
+#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
+/* The first MBO ANQP-element can be included in the optimized bitmap. */
+#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
+ (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+
+struct gas_dialog_info {
+ u8 valid;
+ struct wpabuf *sd_resp; /* Fragmented response */
+ u8 dialog_token;
+ size_t sd_resp_pos; /* Offset in sd_resp */
+ u8 sd_frag_id;
+ int prot; /* whether Protected Dual of Public Action frame is used */
+ int dpp; /* whether this is a DPP Config Response */
+};
+
+struct hostapd_data;
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+ u8 dialog_token);
+void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
+
+int gas_serv_init(struct hostapd_data *hapd);
+void gas_serv_deinit(struct hostapd_data *hapd);
+
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+ const u8 *sa, u8 dialog_token,
+ int prot, struct wpabuf *buf, int freq);
+
+#endif /* GAS_SERV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.c
new file mode 100644
index 0000000..5316203
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.c
@@ -0,0 +1,4460 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2021, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/crc32.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
+#include "radius/radius_client.h"
+#include "radius/radius_das.h"
+#include "eap_server/tncs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "authsrv.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "ap_list.h"
+#include "beacon.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "vlan_init.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
+#include "hw_features.h"
+#include "wpa_auth_glue.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.h"
+#include "bss_load.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+#include "ndisc_snoop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+#include "fils_hlp.h"
+#include "acs.h"
+#include "hs20.h"
+#include "airtime_policy.h"
+#include "wpa_auth_kay.h"
+#include "hw_features.h"
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+#ifdef CONFIG_WEP
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
+#endif /* CONFIG_WEP */
+static int setup_interface2(struct hostapd_iface *iface);
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+ void *timeout_ctx);
+#ifdef CONFIG_IEEE80211AX
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+ void *user_ctx);
+#endif /* CONFIG_IEEE80211AX */
+
+
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx)
+{
+ size_t i;
+ int ret;
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (!interfaces->iface[i])
+ continue;
+ ret = cb(interfaces->iface[i], ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
+{
+ if (hapd->iconf->mbssid)
+ return hapd->iface->bss[0];
+
+ return hapd;
+}
+
+
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
+{
+ if (hapd->iconf->mbssid) {
+ size_t i;
+
+ for (i = 1; i < hapd->iface->num_bss; i++)
+ if (hapd->iface->bss[i] == hapd)
+ return i;
+ }
+
+ return 0;
+}
+
+
+void hostapd_reconfig_encryption(struct hostapd_data *hapd)
+{
+ if (hapd->wpa_auth)
+ return;
+
+ hostapd_set_privacy(hapd, 0);
+#ifdef CONFIG_WEP
+ hostapd_setup_encryption(hapd->conf->iface, hapd);
+#endif /* CONFIG_WEP */
+}
+
+
+static void hostapd_reload_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_ssid *ssid;
+
+ if (!hapd->started)
+ return;
+
+ if (hapd->conf->wmm_enabled < 0)
+ hapd->conf->wmm_enabled = hapd->iconf->ieee80211n |
+ hapd->iconf->ieee80211ax;
+
+#ifndef CONFIG_NO_RADIUS
+ radius_client_reconfig(hapd->radius, hapd->conf->radius);
+#endif /* CONFIG_NO_RADIUS */
+
+ ssid = &hapd->conf->ssid;
+
+ hostapd_set_freq(hapd, hapd->iconf->hw_mode, hapd->iface->freq,
+ hapd->iconf->channel,
+ hapd->iconf->enable_edmg,
+ hapd->iconf->edmg_channel,
+ hapd->iconf->ieee80211n,
+ hapd->iconf->ieee80211ac,
+ hapd->iconf->ieee80211ax,
+ hapd->iconf->ieee80211be,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf),
+ hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf));
+
+ if (hapd->iface->current_mode) {
+ if (hostapd_prepare_rates(hapd->iface, hapd->iface->current_mode)) {
+ wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Failed to prepare rates table.");
+ }
+ }
+
+ if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
+ ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
+ /*
+ * Force PSK to be derived again since SSID or passphrase may
+ * have changed.
+ */
+ hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
+ }
+ if (hostapd_setup_wpa_psk(hapd->conf)) {
+ wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
+ "after reloading configuration");
+ }
+
+ if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+ else
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+ if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
+ hostapd_setup_wpa(hapd);
+ if (hapd->wpa_auth)
+ wpa_init_keys(hapd->wpa_auth);
+ } else if (hapd->conf->wpa) {
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ hostapd_reconfig_wpa(hapd);
+ wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+ if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
+ wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+ "the kernel driver.");
+ } else if (hapd->wpa_auth) {
+ wpa_deinit(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+ hostapd_set_privacy(hapd, 0);
+#ifdef CONFIG_WEP
+ hostapd_setup_encryption(hapd->conf->iface, hapd);
+#endif /* CONFIG_WEP */
+ hostapd_set_generic_elem(hapd, (u8 *) "", 0);
+ }
+
+ ieee802_11_set_beacon(hapd);
+ hostapd_update_wps(hapd);
+
+ if (hapd->conf->ssid.ssid_set &&
+ hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len)) {
+ wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+ /* try to continue */
+ }
+ wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+static void hostapd_clear_old_bss(struct hostapd_data *bss)
+{
+ wpa_printf(MSG_DEBUG, "BSS %s changed - clear old state",
+ bss->conf->iface);
+
+ /*
+ * Deauthenticate all stations since the new configuration may not
+ * allow them to use the BSS anymore.
+ */
+ hostapd_flush_old_stations(bss, WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_WEP
+ hostapd_broadcast_wep_clear(bss);
+#endif /* CONFIG_WEP */
+
+#ifndef CONFIG_NO_RADIUS
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(bss->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++)
+ hostapd_clear_old_bss(iface->bss[j]);
+}
+
+
+static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ struct hostapd_config *oldconf)
+{
+ size_t i;
+
+ if (newconf->config_id != oldconf->config_id)
+ if (strcmp(newconf->config_id, oldconf->config_id))
+ return 1;
+
+ if (newconf->num_bss != oldconf->num_bss)
+ return 1;
+
+ for (i = 0; i < newconf->num_bss; i++) {
+ if (os_strcmp(newconf->bss[i]->iface,
+ oldconf->bss[i]->iface) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
+{
+ int num_sta = 0;
+ int i;
+
+ for (i = 0; i < iface->num_bss; i++)
+ num_sta += iface->bss[i]->num_sta;
+
+ return num_sta;
+}
+
+
+int hostapd_check_max_sta(struct hostapd_data *hapd)
+{
+ if (hapd->num_sta >= hapd->conf->max_num_sta)
+ return 1;
+
+ if (hapd->iconf->max_num_sta &&
+ hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
+ return 1;
+
+ return 0;
+}
+
+static void hostapd_iface_reload_acs(struct hostapd_config *newconf, struct hostapd_config *oldconf)
+{
+ newconf->channel = oldconf->channel;
+ newconf->secondary_channel = oldconf->secondary_channel;
+
+#ifdef CONFIG_IEEE80211BE
+ if (newconf->ieee80211be && oldconf->ieee80211be)
+ {
+ newconf->eht_oper_chwidth = oldconf->eht_oper_chwidth;
+ newconf->eht_oper_centr_freq_seg0_idx = oldconf->eht_oper_centr_freq_seg0_idx;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AX
+ if (newconf->ieee80211ax && oldconf->ieee80211ax)
+ {
+ newconf->he_oper_chwidth = oldconf->he_oper_chwidth;
+ newconf->he_oper_centr_freq_seg0_idx = oldconf->he_oper_centr_freq_seg0_idx;
+ newconf->he_oper_centr_freq_seg1_idx = oldconf->he_oper_centr_freq_seg1_idx;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ newconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
+ newconf->vht_oper_centr_freq_seg0_idx = oldconf->vht_oper_centr_freq_seg0_idx;
+ newconf->vht_oper_centr_freq_seg1_idx = oldconf->vht_oper_centr_freq_seg1_idx;
+ return;
+}
+
+int hostapd_reload_config(struct hostapd_iface *iface, int reconf)
+{
+ struct hapd_interfaces *interfaces = iface->interfaces;
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+ int i;
+
+ if (iface->config_fname == NULL) {
+ /* Only in-memory config in use - assume it has been updated */
+ hostapd_clear_old(iface);
+ for (j = 0; j < iface->num_bss; j++)
+ hostapd_reload_bss(iface->bss[j]);
+ return 0;
+ }
+
+ if (iface->interfaces == NULL ||
+ iface->interfaces->config_read_cb == NULL)
+ return -1;
+ newconf = iface->interfaces->config_read_cb(iface->config_fname);
+ if (newconf == NULL)
+ return -1;
+ wpa_printf(MSG_INFO, "hostapd_reload_config, newconf, channel:%d, secondary_channel:%d, bw: %d, centr_freq_seg0_idx:%d, centr_freq_seg1_idx:%d",
+ newconf->channel, newconf->secondary_channel,
+ hostapd_get_oper_chwidth(newconf),
+ hostapd_get_oper_centr_freq_seg0_idx(newconf),
+ hostapd_get_oper_centr_freq_seg1_idx(newconf));
+
+ oldconf = hapd->iconf;
+ wpa_printf(MSG_INFO, "hostapd_reload_config, oldconf, channel:%d, secondary_channel:%d, bw: %d, centr_freq_seg0_idx:%d, centr_freq_seg1_idx:%d",
+ oldconf->channel, oldconf->secondary_channel,
+ hostapd_get_oper_chwidth(oldconf),
+ hostapd_get_oper_centr_freq_seg0_idx(oldconf),
+ hostapd_get_oper_centr_freq_seg1_idx(oldconf));
+
+ if (hostapd_iface_conf_changed(newconf, oldconf)) {
+ char *fname;
+ int res;
+
+ if (reconf)
+ return -1;
+
+ hostapd_clear_old(iface);
+
+ wpa_printf(MSG_DEBUG,
+ "Configuration changes include interface/BSS modification - force full disable+enable sequence");
+ fname = os_strdup(iface->config_fname);
+ if (!fname) {
+ hostapd_config_free(newconf);
+ return -1;
+ }
+ hostapd_remove_iface(interfaces, hapd->conf->iface);
+ iface = hostapd_init(interfaces, fname);
+ os_free(fname);
+ hostapd_config_free(newconf);
+ if (!iface) {
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize interface on config reload");
+ return -1;
+ }
+ iface->interfaces = interfaces;
+ interfaces->iface[interfaces->count] = iface;
+ interfaces->count++;
+ res = hostapd_enable_iface(iface);
+ if (res < 0)
+ wpa_printf(MSG_ERROR,
+ "Failed to enable interface on config reload");
+ return res;
+ } else {
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (!hapd->config_id || strcmp(hapd->config_id, newconf->bss[j]->config_id)) {
+ hostapd_flush_old_stations(iface->bss[j],
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_WEP
+ hostapd_broadcast_wep_clear(iface->bss[j]);
+#endif
+
+#ifndef CONFIG_NO_RADIUS
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+ wpa_printf(MSG_INFO, "bss %zu changed", j);
+ }
+ }
+ }
+
+ // Avoid channel=0 when reload config using ACS
+ if ((newconf->channel == 0) && (newconf->channel != oldconf->channel))
+ {
+ hostapd_iface_reload_acs(newconf, oldconf);
+ }
+ iface->conf = newconf;
+ wpa_printf(MSG_INFO, "hostapd_reload_config, iface->conf, channel:%d, secondary_channel:%d, bw: %d, centr_freq_seg0_idx:%d, centr_freq_seg1_idx:%d",
+ iface->conf->channel, iface->conf->secondary_channel,
+ hostapd_get_oper_chwidth(iface->conf),
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode = &iface->hw_features[i];
+ if (mode->mode == iface->conf->hw_mode) {
+ iface->current_mode = mode;
+ break;
+ }
+ }
+
+ if (iface->conf->channel)
+ iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hapd->config_id) {
+ os_free(hapd->config_id);
+ hapd->config_id = NULL;
+ }
+ if (newconf->bss[j]->config_id)
+ hapd->config_id = strdup(newconf->bss[j]->config_id);
+ if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
+ os_strcmp(hapd->conf->config_id,
+ newconf->bss[j]->config_id) != 0)
+ hostapd_clear_old_bss(hapd);
+ hapd->iconf = newconf;
+ hapd->conf = newconf->bss[j];
+ hostapd_reload_bss(hapd);
+ }
+
+ hostapd_config_free(oldconf);
+
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WEP
+
+static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
+ const char *ifname)
+{
+ int i;
+
+ if (!ifname || !hapd->drv_priv)
+ return;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0,
+ 0, NULL, 0, NULL, 0, KEY_FLAG_GROUP)) {
+ wpa_printf(MSG_DEBUG, "Failed to clear default "
+ "encryption keys (ifname=%s keyidx=%d)",
+ ifname, i);
+ }
+ }
+ if (hapd->conf->ieee80211w) {
+ for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
+ NULL, i, 0, 0, NULL,
+ 0, NULL, 0, KEY_FLAG_GROUP)) {
+ wpa_printf(MSG_DEBUG, "Failed to clear "
+ "default mgmt encryption keys "
+ "(ifname=%s keyidx=%d)", ifname, i);
+ }
+ }
+ }
+}
+
+
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd)
+{
+ hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface);
+ return 0;
+}
+
+
+static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
+{
+ int errors = 0, idx;
+ struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+ idx = ssid->wep.idx;
+ if (ssid->wep.default_len && ssid->wep.key[idx] &&
+ hostapd_drv_set_key(hapd->conf->iface,
+ hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, 0,
+ 1, NULL, 0, ssid->wep.key[idx],
+ ssid->wep.len[idx],
+ KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
+ wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
+ errors++;
+ }
+
+ return errors;
+}
+
+#endif /* CONFIG_WEP */
+
+
+static void hostapd_clear_drv_priv(struct hostapd_data *hapd)
+{
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
+
+ if (hapd->iface == iface)
+ continue;
+
+ if (iface && iface->bss && iface->bss[0] && iface->bss[0]->mld_first_bss && /* Avoid null pointer by Liangyu Chu*/
+ iface->bss[0]->mld_first_bss == hapd)
+ iface->bss[0]->drv_priv = NULL;
+ }
+
+ hapd->drv_priv = NULL;
+}
+
+
+void hostapd_free_hapd_data(struct hostapd_data *hapd)
+{
+ os_free(hapd->probereq_cb);
+ hapd->probereq_cb = NULL;
+ hapd->num_probereq_cb = 0;
+
+#ifdef CONFIG_P2P
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = NULL;
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+ if (!hapd->started) {
+ wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
+ __func__, hapd->conf ? hapd->conf->iface : "N/A");
+ return;
+ }
+ hapd->started = 0;
+ hapd->beacon_set_done = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
+ hostapd_ubus_free_bss(hapd);
+ accounting_deinit(hapd);
+ hostapd_deinit_wpa(hapd);
+ vlan_deinit(hapd);
+ hostapd_acl_deinit(hapd);
+#ifndef CONFIG_NO_RADIUS
+ if (!hapd->mld_first_bss) {
+ radius_client_deinit(hapd->radius);
+ radius_das_deinit(hapd->radius_das);
+ }
+ hapd->radius = NULL;
+ hapd->radius_das = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+ hostapd_deinit_wps(hapd);
+ ieee802_1x_dealloc_kay_sm_hapd(hapd);
+#ifdef CONFIG_DPP
+ hostapd_dpp_deinit(hapd);
+ gas_query_ap_deinit(hapd->gas);
+ hapd->gas = NULL;
+#endif /* CONFIG_DPP */
+
+ authsrv_deinit(hapd);
+
+ if (hapd->interface_added) {
+ hapd->interface_added = 0;
+ if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+ wpa_printf(MSG_WARNING,
+ "Failed to remove BSS interface %s",
+ hapd->conf->iface);
+ hapd->interface_added = 1;
+ } else {
+ /*
+ * Since this was a dynamically added interface, the
+ * driver wrapper may have removed its internal instance
+ * and hapd->drv_priv is not valid anymore.
+ */
+ hostapd_clear_drv_priv(hapd);
+ }
+ }
+
+ wpabuf_free(hapd->time_adv);
+ hapd->time_adv = NULL;
+
+#ifdef CONFIG_INTERWORKING
+ gas_serv_deinit(hapd);
+#endif /* CONFIG_INTERWORKING */
+
+ bss_load_update_deinit(hapd);
+ ndisc_snoop_deinit(hapd);
+ dhcp_snoop_deinit(hapd);
+ x_snoop_deinit(hapd);
+
+#ifdef CONFIG_SQLITE
+ bin_clear_free(hapd->tmp_eap_user.identity,
+ hapd->tmp_eap_user.identity_len);
+ bin_clear_free(hapd->tmp_eap_user.password,
+ hapd->tmp_eap_user.password_len);
+ os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_MESH
+ wpabuf_free(hapd->mesh_pending_auth);
+ hapd->mesh_pending_auth = NULL;
+ /* handling setup failure is already done */
+ hapd->setup_complete_cb = NULL;
+#endif /* CONFIG_MESH */
+
+ hostapd_clean_rrm(hapd);
+ fils_hlp_deinit(hapd);
+
+#ifdef CONFIG_OCV
+ eloop_cancel_timeout(hostapd_ocv_check_csa_sa_query, hapd, NULL);
+#endif /* CONFIG_OCV */
+
+#ifdef CONFIG_SAE
+ {
+ struct hostapd_sae_commit_queue *q;
+
+ while ((q = dl_list_first(&hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue,
+ list))) {
+ dl_list_del(&q->list);
+ os_free(q);
+ }
+ }
+ eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_IEEE80211AX
+ eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211AX */
+}
+
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * Most of the modules that are initialized in hostapd_setup_bss() are
+ * deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
+ hapd->conf ? hapd->conf->iface : "N/A");
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->ctrl_iface_deinit) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
+ hapd->iface->interfaces->ctrl_iface_deinit(hapd);
+ }
+ hostapd_free_hapd_data(hapd);
+}
+
+
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+ struct hostapd_sta_info *info;
+
+ if (!iface->num_sta_seen)
+ return;
+
+ while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+ list))) {
+ dl_list_del(&info->list);
+ iface->num_sta_seen--;
+ sta_track_del(info);
+ }
+}
+
+
+void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+ if (iface->current_mode)
+ acs_cleanup(iface);
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = NULL;
+ iface->current_mode = NULL;
+ os_free(iface->current_rates);
+ iface->current_rates = NULL;
+ os_free(iface->basic_rates);
+ iface->basic_rates = NULL;
+ iface->cac_started = 0;
+ ap_list_deinit(iface);
+ sta_track_deinit(iface);
+ airtime_policy_update_deinit(iface);
+}
+
+
+/**
+ * hostapd_cleanup_iface - Complete per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called after per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ NULL);
+
+ hostapd_cleanup_iface_partial(iface);
+ hostapd_config_free(iface->conf);
+ iface->conf = NULL;
+
+ os_free(iface->config_fname);
+ os_free(iface->bss);
+ wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
+ os_free(iface);
+}
+
+
+#ifdef CONFIG_WEP
+
+static void hostapd_clear_wep(struct hostapd_data *hapd)
+{
+ if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) {
+ hostapd_set_privacy(hapd, 0);
+ hostapd_broadcast_wep_clear(hapd);
+ }
+}
+
+
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
+{
+ int i;
+
+ hostapd_broadcast_wep_set(hapd);
+
+ if (hapd->conf->ssid.wep.default_len) {
+ hostapd_set_privacy(hapd, 1);
+ return 0;
+ }
+
+ /*
+ * When IEEE 802.1X is not enabled, the driver may need to know how to
+ * set authentication algorithms for static WEP.
+ */
+ hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
+
+ for (i = 0; i < 4; i++) {
+ if (hapd->conf->ssid.wep.key[i] &&
+ hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, 0,
+ i == hapd->conf->ssid.wep.idx, NULL, 0,
+ hapd->conf->ssid.wep.key[i],
+ hapd->conf->ssid.wep.len[i],
+ i == hapd->conf->ssid.wep.idx ?
+ KEY_FLAG_GROUP_RX_TX_DEFAULT :
+ KEY_FLAG_GROUP_RX_TX)) {
+ wpa_printf(MSG_WARNING, "Could not set WEP "
+ "encryption.");
+ return -1;
+ }
+ if (hapd->conf->ssid.wep.key[i] &&
+ i == hapd->conf->ssid.wep.idx)
+ hostapd_set_privacy(hapd, 1);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_WEP */
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
+{
+ int ret = 0;
+ u8 addr[ETH_ALEN];
+
+ if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
+ return 0;
+
+ if (!hapd->iface->driver_ap_teardown) {
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Flushing old station entries");
+
+ if (hostapd_flush(hapd)) {
+ wpa_msg(hapd->msg_ctx, MSG_WARNING,
+ "Could not connect to kernel driver");
+ ret = -1;
+ }
+ }
+ if (hapd->conf && hapd->conf->broadcast_deauth) {
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+ }
+ hostapd_free_stas(hapd);
+
+ return ret;
+}
+
+
+void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+{
+ hostapd_free_stas(hapd);
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+#ifdef CONFIG_WEP
+ hostapd_clear_wep(hapd);
+#endif /* CONFIG_WEP */
+}
+
+
+/**
+ * hostapd_validate_bssid_configuration - Validate BSSID configuration
+ * @iface: Pointer to interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to validate that the configured BSSIDs are valid.
+ */
+static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
+{
+ u8 mask[ETH_ALEN] = { 0 };
+ struct hostapd_data *hapd = iface->bss[0];
+ unsigned int i = iface->conf->num_bss, bits = 0, j;
+ int auto_addr = 0;
+
+ if (hostapd_drv_none(hapd))
+ return 0;
+
+ if (iface->conf->use_driver_iface_addr)
+ return 0;
+
+ /* Generate BSSID mask that is large enough to cover the BSSIDs. */
+
+ /* Determine the bits necessary to cover the number of BSSIDs. */
+ for (i--; i; i >>= 1)
+ bits++;
+
+ /* Determine the bits necessary to any configured BSSIDs,
+ if they are higher than the number of BSSIDs. */
+ for (j = 0; j < iface->conf->num_bss; j++) {
+ if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) {
+ if (j)
+ auto_addr++;
+ continue;
+ }
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ mask[i] |=
+ iface->conf->bss[j]->bssid[i] ^
+ hapd->own_addr[i];
+ }
+ }
+
+ if (!auto_addr)
+ goto skip_mask_ext;
+
+ for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
+ ;
+ j = 0;
+ if (i < ETH_ALEN) {
+ j = (5 - i) * 8;
+
+ while (mask[i] != 0) {
+ mask[i] >>= 1;
+ j++;
+ }
+ }
+
+ if (bits < j)
+ bits = j;
+
+ if (bits > 40) {
+ wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
+ bits);
+ return -1;
+ }
+
+ os_memset(mask, 0xff, ETH_ALEN);
+ j = bits / 8;
+ for (i = 5; i > 5 - j; i--)
+ mask[i] = 0;
+ j = bits % 8;
+ while (j) {
+ j--;
+ mask[i] <<= 1;
+ }
+
+skip_mask_ext:
+ wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
+ (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
+
+ if (!auto_addr)
+ return 0;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
+ wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
+ " for start address " MACSTR ".",
+ MAC2STR(mask), MAC2STR(hapd->own_addr));
+ wpa_printf(MSG_ERROR, "Start address must be the "
+ "first address in the block (i.e., addr "
+ "AND mask == addr).");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int mac_in_conf(struct hostapd_config *conf, const void *a)
+{
+ size_t i;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr)
+{
+ if (attr->nas_identifier &&
+ (!hapd->conf->nas_identifier ||
+ os_strlen(hapd->conf->nas_identifier) !=
+ attr->nas_identifier_len ||
+ os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
+ attr->nas_identifier_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
+ return 1;
+ }
+
+ if (attr->nas_ip_addr &&
+ (hapd->conf->own_ip_addr.af != AF_INET ||
+ os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
+ 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
+ return 1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (attr->nas_ipv6_addr &&
+ (hapd->conf->own_ip_addr.af != AF_INET6 ||
+ os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
+ != 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
+ return 1;
+ }
+#endif /* CONFIG_IPV6 */
+
+ return 0;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr,
+ int *multi)
+{
+ struct sta_info *selected, *sta;
+ char buf[128];
+ int num_attr = 0;
+ int count;
+
+ *multi = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next)
+ sta->radius_das_match = 1;
+
+ if (attr->sta_addr) {
+ num_attr++;
+ sta = ap_get_sta(hapd, attr->sta_addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No Calling-Station-Id match");
+ return NULL;
+ }
+
+ selected = sta;
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta != selected)
+ sta->radius_das_match = 0;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+ }
+
+ if (attr->acct_session_id) {
+ num_attr++;
+ if (attr->acct_session_id_len != 16) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Session-Id cannot match");
+ return NULL;
+ }
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
+ os_snprintf(buf, sizeof(buf), "%016llX",
+ (unsigned long long) sta->acct_session_id);
+ if (os_memcmp(attr->acct_session_id, buf, 16) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
+ }
+
+ if (attr->acct_multi_session_id) {
+ num_attr++;
+ if (attr->acct_multi_session_id_len != 16) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+ return NULL;
+ }
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
+ if (!sta->eapol_sm ||
+ !sta->eapol_sm->acct_multi_session_id) {
+ sta->radius_das_match = 0;
+ continue;
+ }
+ os_snprintf(buf, sizeof(buf), "%016llX",
+ (unsigned long long)
+ sta->eapol_sm->acct_multi_session_id);
+ if (os_memcmp(attr->acct_multi_session_id, buf, 16) !=
+ 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id match");
+ }
+
+ if (attr->cui) {
+ num_attr++;
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ struct wpabuf *cui;
+
+ if (!sta->radius_das_match)
+ continue;
+ cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (!cui || wpabuf_len(cui) != attr->cui_len ||
+ os_memcmp(wpabuf_head(cui), attr->cui,
+ attr->cui_len) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Chargeable-User-Identity match");
+ }
+
+ if (attr->user_name) {
+ num_attr++;
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ u8 *identity;
+ size_t identity_len;
+
+ if (!sta->radius_das_match)
+ continue;
+ identity = ieee802_1x_get_identity(sta->eapol_sm,
+ &identity_len);
+ if (!identity ||
+ identity_len != attr->user_name_len ||
+ os_memcmp(identity, attr->user_name, identity_len)
+ != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after User-Name check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: User-Name match");
+ }
+
+ if (num_attr == 0) {
+ /*
+ * In theory, we could match all current associations, but it
+ * seems safer to just reject requests that do not include any
+ * session identification attributes.
+ */
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No session identification attributes included");
+ return NULL;
+ }
+
+ selected = NULL;
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta->radius_das_match) {
+ if (selected) {
+ *multi = 1;
+ return NULL;
+ }
+ selected = sta;
+ }
+ }
+
+ return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr)
+{
+ if (!hapd->wpa_auth)
+ return -1;
+ return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ int multi;
+
+ if (hostapd_das_nas_mismatch(hapd, attr))
+ return RADIUS_DAS_NAS_MISMATCH;
+
+ sta = hostapd_das_find_sta(hapd, attr, &multi);
+ if (sta == NULL) {
+ if (multi) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Multiple sessions match - not supported");
+ return RADIUS_DAS_MULTI_SESSION_MATCH;
+ }
+ if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: PMKSA cache entry matched");
+ return RADIUS_DAS_SUCCESS;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+ " - disconnecting", MAC2STR(sta->addr));
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+ return RADIUS_DAS_SUCCESS;
+}
+
+
+#ifdef CONFIG_HS20
+static enum radius_das_res
+hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ int multi;
+
+ if (hostapd_das_nas_mismatch(hapd, attr))
+ return RADIUS_DAS_NAS_MISMATCH;
+
+ sta = hostapd_das_find_sta(hapd, attr, &multi);
+ if (!sta) {
+ if (multi) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Multiple sessions match - not supported");
+ return RADIUS_DAS_MULTI_SESSION_MATCH;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+ " - CoA", MAC2STR(sta->addr));
+
+ if (attr->hs20_t_c_filtering) {
+ if (attr->hs20_t_c_filtering[0] & BIT(0)) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
+ return RADIUS_DAS_COA_FAILED;
+ }
+
+ hs20_t_c_filtering(hapd, sta, 0);
+ }
+
+ return RADIUS_DAS_SUCCESS;
+}
+#else /* CONFIG_HS20 */
+#define hostapd_das_coa NULL
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_SQLITE
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+ char cmd[128];
+
+ os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+ return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_radius_attributes(sqlite3 *db)
+{
+ char *err = NULL;
+ const char *sql =
+ "CREATE TABLE radius_attributes("
+ " id INTEGER PRIMARY KEY,"
+ " sta TEXT,"
+ " reqtype TEXT,"
+ " attr TEXT"
+ ");"
+ "CREATE INDEX idx_sta_reqtype ON radius_attributes(sta,reqtype);";
+
+ wpa_printf(MSG_DEBUG,
+ "Adding database table for RADIUS attribute information");
+ if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+ wpa_printf(MSG_ERROR, "SQLite error: %s", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
+#endif /* CONFIG_NO_RADIUS */
+
+
+static int hostapd_start_beacon(struct hostapd_data *hapd,
+ bool flush_old_stations)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ return -1;
+
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+
+ /* Should any previously associated STA not have noticed that
+ * the AP had stopped and restarted, send one more
+ * deauthentication notification now that the AP is ready to
+ * operate. */
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations at BSS start");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+ hostapd_ubus_add_bss(hapd);
+
+ return 0;
+}
+
+
+/**
+ * hostapd_setup_bss - Per-BSS setup (initialization)
+ * @hapd: Pointer to BSS data
+ * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ * but interface may exist
+ * @start_beacon: Whether Beacon frame template should be configured and
+ * transmission of Beaconf rames started at this time. This is used when
+ * MBSSID element is enabled where the information regarding all BSSes
+ * should be retrieved before configuring the Beacon frame template. The
+ * calling functions are responsible for configuring the Beacon frame
+ * explicitly if this is set to false.
+ *
+ * This function is used to initialize all per-BSS data structures and
+ * resources. This gets called in a loop for each BSS when an interface is
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ bool start_beacon)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 ssid[SSID_MAX_LEN + 1];
+ int ssid_len, set_ssid;
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+ int flush_old_stations = 1;
+
+ if (hapd->mld_first_bss)
+ wpa_printf(MSG_DEBUG,
+ "MLD: %s: Setting non-first BSS", __func__);
+
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ __func__, hapd, conf->iface, first);
+
+#ifdef EAP_SERVER_TNC
+ if (conf->tnc && tncs_global_init() < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+ return -1;
+ }
+#endif /* EAP_SERVER_TNC */
+
+ if (hapd->started) {
+ wpa_printf(MSG_ERROR, "%s: Interface %s was already started",
+ __func__, conf->iface);
+ return -1;
+ }
+ hapd->started = 1;
+
+ if (!first || first == -1) {
+ u8 *addr = hapd->own_addr;
+
+ if (!is_zero_ether_addr(conf->bssid)) {
+ /* Allocate the configured BSSID. */
+ os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
+
+ if (hostapd_mac_comp(hapd->own_addr,
+ hapd->iface->bss[0]->own_addr) ==
+ 0) {
+ wpa_printf(MSG_ERROR, "BSS '%s' may not have "
+ "BSSID set to the MAC address of "
+ "the radio", conf->iface);
+ return -1;
+ }
+ } else if (hapd->iconf->use_driver_iface_addr) {
+ addr = NULL;
+ } else {
+ /* Allocate the next available BSSID. */
+ do {
+ inc_byte_array(hapd->own_addr, ETH_ALEN);
+ } while (mac_in_conf(hapd->iconf, hapd->own_addr));
+ }
+
+ hapd->interface_added = 1;
+ if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+ conf->iface, addr, hapd,
+ &hapd->drv_priv, force_ifname, if_addr,
+ conf->bridge[0] ? conf->bridge : NULL,
+ first == -1)) {
+ wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ MACSTR ")", MAC2STR(hapd->own_addr));
+ hapd->interface_added = 0;
+ return -1;
+ }
+
+ if (!addr)
+ os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
+ }
+
+ if (conf->wmm_enabled < 0)
+ conf->wmm_enabled = hapd->iconf->ieee80211n |
+ hapd->iconf->ieee80211ax;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (is_zero_ether_addr(conf->r1_key_holder))
+ os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_MESH
+ if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
+ flush_old_stations = 0;
+#endif /* CONFIG_MESH */
+
+ if (flush_old_stations)
+ hostapd_flush(hapd);
+ hostapd_set_privacy(hapd, 0);
+
+#ifdef CONFIG_WEP
+ if (!hostapd_drv_nl80211(hapd))
+ hostapd_broadcast_wep_clear(hapd);
+ if (hostapd_setup_encryption(conf->iface, hapd))
+ return -1;
+#endif /* CONFIG_WEP */
+
+ /*
+ * Fetch the SSID from the system and use it or,
+ * if one was specified in the config file, verify they
+ * match.
+ */
+ ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
+ if (ssid_len < 0) {
+ wpa_printf(MSG_ERROR, "Could not read SSID from system");
+ return -1;
+ }
+ if (conf->ssid.ssid_set) {
+ /*
+ * If SSID is specified in the config file and it differs
+ * from what is being used then force installation of the
+ * new SSID.
+ */
+ set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
+ os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
+ } else {
+ /*
+ * No SSID in the config file; just use the one we got
+ * from the system.
+ */
+ set_ssid = 0;
+ conf->ssid.ssid_len = ssid_len;
+ os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
+ }
+
+ /*
+ * Short SSID calculation is identical to FCS and it is defined in
+ * IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
+ */
+ conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid,
+ conf->ssid.ssid_len);
+
+ if (!hostapd_drv_none(hapd)) {
+ wpa_printf(MSG_DEBUG, "Using interface %s with hwaddr " MACSTR
+ " and ssid \"%s\"",
+ conf->iface, MAC2STR(hapd->own_addr),
+ wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
+ }
+
+ if (hostapd_setup_wpa_psk(conf)) {
+ wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
+ return -1;
+ }
+
+ /* Set SSID for the kernel driver (to be used in beacon and probe
+ * response frames) */
+ if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
+ conf->ssid.ssid_len)) {
+ wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+ return -1;
+ }
+
+ if (wpa_debug_level <= MSG_MSGDUMP)
+ conf->radius->msg_dumps = 1;
+#ifndef CONFIG_NO_RADIUS
+
+#ifdef CONFIG_SQLITE
+ if (conf->radius_req_attr_sqlite) {
+ if (sqlite3_open(conf->radius_req_attr_sqlite,
+ &hapd->rad_attr_db)) {
+ wpa_printf(MSG_ERROR, "Could not open SQLite file '%s'",
+ conf->radius_req_attr_sqlite);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Opening RADIUS attribute database: %s",
+ conf->radius_req_attr_sqlite);
+ if (!db_table_exists(hapd->rad_attr_db, "radius_attributes") &&
+ db_table_create_radius_attributes(hapd->rad_attr_db) < 0)
+ return -1;
+ }
+#endif /* CONFIG_SQLITE */
+
+ if (!hapd->mld_first_bss) {
+ hapd->radius = radius_client_init(hapd, conf->radius);
+ if (!hapd->radius) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS client initialization failed.");
+ return -1;
+ }
+
+ if (conf->radius_das_port) {
+ struct radius_das_conf das_conf;
+
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
+ das_conf.nas_identifier = conf->nas_identifier;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+ das_conf.client_addr = &conf->radius_das_client_addr;
+ das_conf.time_window = conf->radius_das_time_window;
+ das_conf.require_event_timestamp =
+ conf->radius_das_require_event_timestamp;
+ das_conf.require_message_authenticator =
+ conf->radius_das_require_message_authenticator;
+ das_conf.ctx = hapd;
+ das_conf.disconnect = hostapd_das_disconnect;
+ das_conf.coa = hostapd_das_coa;
+ hapd->radius_das = radius_das_init(&das_conf);
+ if (!hapd->radius_das) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS DAS initialization failed.");
+ return -1;
+ }
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using RADIUS client of the first BSS");
+ hapd->radius = hapd->mld_first_bss->radius;
+ hapd->radius_das = hapd->mld_first_bss->radius_das;
+ }
+#endif /* CONFIG_NO_RADIUS */
+
+ if (hostapd_acl_init(hapd)) {
+ wpa_printf(MSG_ERROR, "ACL initialization failed.");
+ return -1;
+ }
+ if (hostapd_init_wps(hapd, conf))
+ return -1;
+
+#ifdef CONFIG_DPP
+ hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
+ if (!hapd->gas)
+ return -1;
+ if (hostapd_dpp_init(hapd))
+ return -1;
+#endif /* CONFIG_DPP */
+
+ if (authsrv_init(hapd) < 0)
+ return -1;
+
+ if (ieee802_1x_init(hapd)) {
+ wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
+ return -1;
+ }
+
+ if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
+ return -1;
+
+ if (accounting_init(hapd)) {
+ wpa_printf(MSG_ERROR, "Accounting initialization failed.");
+ return -1;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ if (gas_serv_init(hapd)) {
+ wpa_printf(MSG_ERROR, "GAS server initialization failed");
+ return -1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ if (conf->qos_map_set_len &&
+ hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+ conf->qos_map_set_len)) {
+ wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
+ return -1;
+ }
+
+ if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+ wpa_printf(MSG_ERROR, "BSS Load initialization failed");
+ return -1;
+ }
+
+ if (conf->bridge[0]) {
+ /* Set explicitly configured bridge parameters that might have
+ * been lost if the interface has been removed out of the
+ * bridge. */
+
+ /* multicast to unicast on bridge ports */
+ if (conf->bridge_multicast_to_unicast)
+ hostapd_drv_br_port_set_attr(
+ hapd, DRV_BR_PORT_ATTR_MCAST2UCAST, 1);
+
+ /* hairpin mode */
+ if (conf->bridge_hairpin)
+ hostapd_drv_br_port_set_attr(
+ hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 1);
+ }
+
+ if (conf->proxy_arp) {
+ if (x_snoop_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Generic snooping infrastructure initialization failed");
+ return -1;
+ }
+
+ if (dhcp_snoop_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "DHCP snooping initialization failed");
+ return -1;
+ }
+
+ if (ndisc_snoop_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Neighbor Discovery snooping initialization failed");
+ return -1;
+ }
+ }
+
+ if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
+ wpa_printf(MSG_ERROR, "VLAN initialization failed.");
+ return -1;
+ }
+
+ if (start_beacon && hostapd_start_beacon(hapd, flush_old_stations) < 0)
+ return -1;
+
+ if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static void hostapd_tx_queue_params(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ int i;
+ struct hostapd_tx_queue_params *p;
+
+#ifdef CONFIG_MESH
+ if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
+ return;
+#endif /* CONFIG_MESH */
+
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ p = &iface->conf->tx_queue[i];
+
+ if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
+ p->cwmax, p->burst)) {
+ wpa_printf(MSG_DEBUG, "Failed to set TX queue "
+ "parameters for queue %d.", i);
+ /* Continue anyway */
+ }
+ }
+}
+
+
+static int hostapd_set_acl_list(struct hostapd_data *hapd,
+ struct mac_acl_entry *mac_acl,
+ int n_entries, u8 accept_acl)
+{
+ struct hostapd_acl_params *acl_params;
+ int i, err;
+
+ acl_params = os_zalloc(sizeof(*acl_params) +
+ (n_entries * sizeof(acl_params->mac_acl[0])));
+ if (!acl_params)
+ return -ENOMEM;
+
+ for (i = 0; i < n_entries; i++)
+ os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr,
+ ETH_ALEN);
+
+ acl_params->acl_policy = accept_acl;
+ acl_params->num_mac_acl = n_entries;
+
+ err = hostapd_drv_set_acl(hapd, acl_params);
+
+ os_free(acl_params);
+
+ return err;
+}
+
+
+int hostapd_set_acl(struct hostapd_data *hapd)
+{
+ struct hostapd_config *conf = hapd->iconf;
+ int err = 0;
+ u8 accept_acl;
+
+ if (hapd->iface->drv_max_acl_mac_addrs == 0)
+ return 0;
+
+ if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+ accept_acl = 1;
+ err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
+ conf->bss[0]->num_accept_mac,
+ accept_acl);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "Failed to set accept acl");
+ return -1;
+ }
+ } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+ accept_acl = 0;
+ err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
+ conf->bss[0]->num_deny_mac,
+ accept_acl);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "Failed to set deny acl");
+ return -1;
+ }
+ }
+ return err;
+}
+
+
+static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+{
+ if (!hapd->iface->interfaces ||
+ !hapd->iface->interfaces->ctrl_iface_init)
+ return 0;
+
+ if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to setup control interface for %s",
+ hapd->conf->iface);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int start_ctrl_iface(struct hostapd_iface *iface)
+{
+ size_t i;
+
+ if (!iface->interfaces || !iface->interfaces->ctrl_iface_init)
+ return 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+ if (iface->interfaces->ctrl_iface_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to setup control interface for %s",
+ hapd->conf->iface);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* When NO_IR flag is set and AP is stopped, clean up BSS parameters without
+ * deinitializing the driver and the control interfaces. A subsequent
+ * REG_CHANGE event can bring the AP back up.
+ */
+static void hostapd_no_ir_cleanup(struct hostapd_data *bss)
+{
+ hostapd_bss_deinit_no_free(bss);
+ hostapd_free_hapd_data(bss);
+ hostapd_cleanup_iface_partial(bss->iface);
+}
+
+
+static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
+ void *ctx)
+{
+ bool all_no_ir, is_6ghz;
+ int i, j;
+ struct hostapd_hw_modes *mode = NULL;
+
+ if (hostapd_get_hw_features(iface))
+ return 0;
+
+ all_no_ir = true;
+ is_6ghz = false;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+
+ if (mode->mode == iface->conf->hw_mode) {
+ if (iface->freq > 0 &&
+ !hw_mode_get_channel(mode, iface->freq, NULL)) {
+ mode = NULL;
+ continue;
+ }
+
+ for (j = 0; j < mode->num_channels; j++) {
+ if (!(mode->channels[j].flag &
+ HOSTAPD_CHAN_NO_IR))
+ all_no_ir = false;
+
+ if (is_6ghz_freq(mode->channels[j].freq))
+ is_6ghz = true;
+ }
+ break;
+ }
+ }
+
+ if (!mode || !is_6ghz)
+ return 0;
+ iface->current_mode = mode;
+
+ if (iface->state == HAPD_IFACE_ENABLED) {
+ if (!all_no_ir) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_freq(iface->current_mode->mode,
+ iface->freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+
+ if (!chan) {
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Could not derive chan from freq");
+ return 0;
+ }
+
+ if (!(chan->flag & HOSTAPD_CHAN_NO_IR))
+ return 0;
+ wpa_printf(MSG_DEBUG,
+ "NO_IR: The current channel has NO_IR flag now, stop AP.");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "NO_IR: All chan in new chanlist are NO_IR, stop AP.");
+ }
+
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ iface->is_no_ir = true;
+ hostapd_drv_stop_ap(iface->bss[0]);
+ hostapd_no_ir_cleanup(iface->bss[0]);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ } else if (iface->state == HAPD_IFACE_NO_IR) {
+ if (all_no_ir) {
+ wpa_printf(MSG_DEBUG,
+ "NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore");
+ return 0;
+ }
+
+ if (!iface->conf->acs) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_freq(iface->current_mode->mode,
+ iface->freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+ if (!chan) {
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Could not derive chan from freq");
+ return 0;
+ }
+
+ /* If the last operating channel is NO_IR, trigger ACS.
+ */
+ if (chan->flag & HOSTAPD_CHAN_NO_IR) {
+ iface->freq = 0;
+ iface->conf->channel = 0;
+ if (acs_init(iface) != HOSTAPD_CHAN_ACS)
+ wpa_printf(MSG_ERROR,
+ "NO_IR: Could not start ACS");
+ return 0;
+ }
+ }
+
+ setup_interface2(iface);
+ }
+
+ return 0;
+}
+
+
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_iface *iface = eloop_ctx;
+
+ if (!iface->wait_channel_update) {
+ wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it");
+ return;
+ }
+
+ /*
+ * It is possible that the existing channel list is acceptable, so try
+ * to proceed.
+ */
+ wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway");
+ setup_interface2(iface);
+}
+
+
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
+{
+ if (initiator == REGDOM_SET_BY_DRIVER) {
+ hostapd_for_each_interface(iface->interfaces,
+ hostapd_no_ir_channel_list_updated,
+ NULL);
+ return;
+ }
+
+ if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Channel list updated - continue setup");
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ setup_interface2(iface);
+}
+
+
+static int setup_interface(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ size_t i;
+
+ /*
+ * It is possible that setup_interface() is called after the interface
+ * was disabled etc., in which case driver_ap_teardown is possibly set
+ * to 1. Clear it here so any other key/station deletion, which is not
+ * part of a teardown flow, would also call the relevant driver
+ * callbacks.
+ */
+ iface->driver_ap_teardown = 0;
+
+ if (!iface->phy[0]) {
+ const char *phy = hostapd_drv_get_radio_name(hapd);
+ if (phy) {
+ wpa_printf(MSG_DEBUG, "phy: %s", phy);
+ os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+ }
+ }
+
+ /*
+ * Make sure that all BSSes get configured with a pointer to the same
+ * driver interface.
+ */
+ for (i = 1; i < iface->num_bss; i++) {
+ iface->bss[i]->driver = hapd->driver;
+ iface->bss[i]->drv_priv = hapd->drv_priv;
+ }
+
+ if (hostapd_validate_bssid_configuration(iface))
+ return -1;
+
+ /*
+ * Initialize control interfaces early to allow external monitoring of
+ * channel setup operations that may take considerable amount of time
+ * especially for DFS cases.
+ */
+ if (start_ctrl_iface(iface))
+ return -1;
+
+ if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+ char country[4], previous_country[4];
+
+ hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE);
+ if (hostapd_get_country(hapd, previous_country) < 0)
+ previous_country[0] = '\0';
+
+ os_memcpy(country, hapd->iconf->country, 3);
+ country[3] = '\0';
+ if (hostapd_set_country(hapd, country) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to set country code");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s",
+ previous_country, country);
+
+ if (os_strncmp(previous_country, country, 2) != 0) {
+ wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update");
+ iface->wait_channel_update = 1;
+ eloop_register_timeout(5, 0,
+ channel_list_update_timeout,
+ iface, NULL);
+ return 0;
+ }
+ }
+
+ return setup_interface2(iface);
+}
+
+
+static int configured_fixed_chan_to_freq(struct hostapd_iface *iface)
+{
+ int freq, i, j;
+
+ if (!iface->conf->channel)
+ return 0;
+ if (iface->conf->op_class) {
+ freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
+ iface->conf->channel);
+ if (freq < 0) {
+ wpa_printf(MSG_INFO,
+ "Could not convert op_class %u channel %u to operating frequency",
+ iface->conf->op_class, iface->conf->channel);
+ return -1;
+ }
+ iface->freq = freq;
+ return 0;
+ }
+
+ /* Old configurations using only 2.4/5/60 GHz bands may not specify the
+ * op_class parameter. Select a matching channel from the configured
+ * mode using the channel parameter for these cases.
+ */
+ for (j = 0; j < iface->num_hw_features; j++) {
+ struct hostapd_hw_modes *mode = &iface->hw_features[j];
+
+ if (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
+ iface->conf->hw_mode != mode->mode)
+ continue;
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if (chan->chan == iface->conf->channel &&
+ !is_6ghz_freq(chan->freq)) {
+ iface->freq = chan->freq;
+ return 0;
+ }
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Could not determine operating frequency");
+ return -1;
+}
+
+
+static void hostapd_set_6ghz_sec_chan(struct hostapd_iface *iface)
+{
+ int bw;
+
+ if (!is_6ghz_op_class(iface->conf->op_class))
+ return;
+
+ bw = op_class_to_bandwidth(iface->conf->op_class);
+ /* Assign the secondary channel if absent in config for
+ * bandwidths > 20 MHz */
+ if (bw >= 40 && !iface->conf->secondary_channel) {
+ if (((iface->conf->channel - 1) / 4) % 2)
+ iface->conf->secondary_channel = -1;
+ else
+ iface->conf->secondary_channel = 1;
+ }
+}
+
+
+static int setup_interface2(struct hostapd_iface *iface)
+{
+ iface->wait_channel_update = 0;
+ iface->is_no_ir = false;
+
+ if (hostapd_get_hw_features(iface)) {
+ /* Not all drivers support this yet, so continue without hw
+ * feature data. */
+ } else {
+ int ret;
+
+ if (iface->conf->acs) {
+ iface->freq = 0;
+ iface->conf->channel = 0;
+ }
+
+ ret = configured_fixed_chan_to_freq(iface);
+ if (ret < 0)
+ goto fail;
+
+ if (iface->conf->op_class) {
+ enum oper_chan_width ch_width;
+
+ ch_width = op_class_to_ch_width(iface->conf->op_class);
+ hostapd_set_oper_chwidth(iface->conf, ch_width);
+ hostapd_set_6ghz_sec_chan(iface);
+ }
+
+ ret = hostapd_select_hw_mode(iface);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "Could not select hw_mode and "
+ "channel. (%d)", ret);
+ goto fail;
+ }
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
+ return 0;
+ }
+ ret = hostapd_check_edmg_capab(iface);
+ if (ret < 0)
+ goto fail;
+ ret = hostapd_check_he_6ghz_capab(iface);
+ if (ret < 0)
+ goto fail;
+ ret = hostapd_check_ht_capab(iface);
+ if (ret < 0)
+ goto fail;
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will "
+ "be completed in a callback");
+ return 0;
+ }
+
+ if (iface->conf->ieee80211h)
+ wpa_printf(MSG_DEBUG, "DFS support is enabled");
+ }
+ return hostapd_setup_interface_complete(iface, 0);
+
+fail:
+ if (iface->is_no_ir) {
+ /* If AP is in NO_IR state, it can be reenabled by the driver
+ * regulatory update and EVENT_CHANNEL_LIST_CHANGED. */
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ return 0;
+ }
+
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (iface->interfaces && iface->interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
+}
+
+
+#ifdef CONFIG_FST
+
+static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hapd->own_addr;
+}
+
+
+static void fst_hostapd_get_channel_info_cb(void *ctx,
+ enum hostapd_hw_mode *hw_mode,
+ u8 *channel)
+{
+ struct hostapd_data *hapd = ctx;
+
+ *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
+}
+
+
+static int fst_hostapd_get_hw_modes_cb(void *ctx,
+ struct hostapd_hw_modes **modes)
+{
+ struct hostapd_data *hapd = ctx;
+
+ *modes = hapd->iface->hw_features;
+ return hapd->iface->num_hw_features;
+}
+
+
+static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+ struct hostapd_data *hapd = ctx;
+
+ if (hapd->iface->fst_ies != fst_ies) {
+ hapd->iface->fst_ies = fst_ies;
+ if (ieee802_11_set_beacon(hapd))
+ wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
+ }
+}
+
+
+static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
+ struct wpabuf *buf)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+ wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ return sta ? sta->mb_ies : NULL;
+}
+
+
+static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (sta) {
+ struct mb_ies_info info;
+
+ if (!mb_ies_info_by_ies(&info, buf, size)) {
+ wpabuf_free(sta->mb_ies);
+ sta->mb_ies = mb_ies_by_info(&info);
+ }
+ }
+}
+
+
+static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
+ bool mb_only)
+{
+ struct sta_info *s = (struct sta_info *) *get_ctx;
+
+ if (mb_only) {
+ for (; s && !s->mb_ies; s = s->next)
+ ;
+ }
+
+ if (s) {
+ *get_ctx = (struct fst_get_peer_ctx *) s->next;
+
+ return s->addr;
+ }
+
+ *get_ctx = NULL;
+ return NULL;
+}
+
+
+static const u8 * fst_hostapd_get_peer_first(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ bool mb_only)
+{
+ struct hostapd_data *hapd = ctx;
+
+ *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
+
+ return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+static const u8 * fst_hostapd_get_peer_next(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ bool mb_only)
+{
+ return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+ struct fst_wpa_obj *iface_obj)
+{
+ os_memset(iface_obj, 0, sizeof(*iface_obj));
+ iface_obj->ctx = hapd;
+ iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
+ iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+ iface_obj->get_hw_modes = fst_hostapd_get_hw_modes_cb;
+ iface_obj->set_ies = fst_hostapd_set_ies_cb;
+ iface_obj->send_action = fst_hostapd_send_action_cb;
+ iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
+ iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
+ iface_obj->get_peer_first = fst_hostapd_get_peer_first;
+ iface_obj->get_peer_next = fst_hostapd_get_peer_next;
+}
+
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_OWE
+
+static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+
+ if (os_strcmp(hapd->conf->owe_transition_ifname,
+ bss->conf->iface) != 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "OWE: ifname=%s found transition mode ifname=%s BSSID "
+ MACSTR " SSID %s",
+ hapd->conf->iface, bss->conf->iface,
+ MAC2STR(bss->own_addr),
+ wpa_ssid_txt(bss->conf->ssid.ssid,
+ bss->conf->ssid.ssid_len));
+ if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
+ is_zero_ether_addr(bss->own_addr))
+ continue;
+
+ os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
+ ETH_ALEN);
+ os_memcpy(hapd->conf->owe_transition_ssid,
+ bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
+ hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
+ wpa_printf(MSG_DEBUG,
+ "OWE: Copied transition mode information");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
+{
+ if (hapd->conf->owe_transition_ssid_len > 0 &&
+ !is_zero_ether_addr(hapd->conf->owe_transition_bssid))
+ return 0;
+
+ /* Find transition mode SSID/BSSID information from a BSS operated by
+ * this hostapd instance. */
+ if (!hapd->iface->interfaces ||
+ !hapd->iface->interfaces->for_each_interface)
+ return hostapd_owe_iface_iter(hapd->iface, hapd);
+ else
+ return hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
+}
+
+
+static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
+{
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+ int res;
+
+ if (!bss->conf->owe_transition_ifname[0])
+ continue;
+ if (bss->iface->state != HAPD_IFACE_ENABLED) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Interface %s state %s - defer beacon update",
+ bss->conf->iface,
+ hostapd_state_text(bss->iface->state));
+ continue;
+ }
+ res = hostapd_owe_trans_get_info(bss);
+ if (res == 0)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "OWE: Matching transition mode interface enabled - update beacon data for %s",
+ bss->conf->iface);
+ ieee802_11_set_beacon(bss);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static void hostapd_owe_update_trans(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_OWE
+ /* Check whether the enabled BSS can complete OWE transition mode
+ * configuration for any pending interface. */
+ if (!iface->interfaces ||
+ !iface->interfaces->for_each_interface)
+ hostapd_owe_iface_iter2(iface, NULL);
+ else
+ iface->interfaces->for_each_interface(
+ iface->interfaces, hostapd_owe_iface_iter2, NULL);
+#endif /* CONFIG_OWE */
+}
+
+
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_iface *iface = eloop_ctx;
+ struct hostapd_data *hapd;
+
+ if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
+ return;
+ hapd = iface->bss[0];
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+}
+
+
+static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ int err)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ size_t j;
+ u8 *prev_addr;
+ int delay_apply_cfg = 0;
+ int res_dfs_offload = 0;
+
+ if (err)
+ goto fail;
+
+ hostapd_ubus_add_iface(iface);
+ wpa_printf(MSG_DEBUG, "Completing interface initialization");
+ if (iface->freq) {
+#ifdef NEED_AP_MLME
+ int res;
+#endif /* NEED_AP_MLME */
+
+ wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d "
+ "Frequency: %d MHz",
+ hostapd_hw_mode_txt(iface->conf->hw_mode),
+ iface->conf->channel, iface->freq);
+
+#ifdef NEED_AP_MLME
+ /* Handle DFS only if it is not offloaded to the driver */
+ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+ /* Check DFS */
+ res = hostapd_handle_dfs(iface);
+ if (res <= 0) {
+ if (res < 0)
+ goto fail;
+ return res;
+ }
+ } else {
+ /* If DFS is offloaded to the driver */
+ res_dfs_offload = hostapd_handle_dfs_offload(iface);
+ if (res_dfs_offload <= 0) {
+ if (res_dfs_offload < 0)
+ goto fail;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Proceed with AP/channel setup");
+ /*
+ * If this is a DFS channel, move to completing
+ * AP setup.
+ */
+ if (res_dfs_offload == 1)
+ goto dfs_offload;
+ /* Otherwise fall through. */
+ }
+ }
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+ if (iface->mconf != NULL) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Mesh configuration will be applied while joining the mesh network",
+ iface->bss[0]->conf->iface);
+ delay_apply_cfg = 1;
+ }
+#endif /* CONFIG_MESH */
+
+ if (!delay_apply_cfg &&
+ hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ hapd->iconf->channel,
+ hapd->iconf->enable_edmg,
+ hapd->iconf->edmg_channel,
+ hapd->iconf->ieee80211n,
+ hapd->iconf->ieee80211ac,
+ hapd->iconf->ieee80211ax,
+ hapd->iconf->ieee80211be,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ hostapd_get_oper_centr_freq_seg0_idx(
+ hapd->iconf),
+ hostapd_get_oper_centr_freq_seg1_idx(
+ hapd->iconf))) {
+ wpa_printf(MSG_ERROR, "Could not set channel for "
+ "kernel driver");
+ goto fail;
+ }
+ }
+
+ if (iface->current_mode) {
+ if (hostapd_prepare_rates(iface, iface->current_mode)) {
+ wpa_printf(MSG_ERROR, "Failed to prepare rates "
+ "table.");
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Failed to prepare rates table.");
+ goto fail;
+ }
+ }
+
+ if (hapd->iconf->rts_threshold >= -1 &&
+ hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
+ hapd->iconf->rts_threshold >= -1) {
+ wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
+ "kernel driver");
+ goto fail;
+ }
+
+ if (hapd->iconf->fragm_threshold >= -1 &&
+ hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) &&
+ hapd->iconf->fragm_threshold != -1) {
+ wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
+ "for kernel driver");
+ goto fail;
+ }
+
+ prev_addr = hapd->own_addr;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (j)
+ os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
+ if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
+ for (;;) {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ if (j == 0)
+ break;
+ j--;
+ }
+ goto fail;
+ }
+ if (is_zero_ether_addr(hapd->conf->bssid))
+ prev_addr = hapd->own_addr;
+ }
+
+ if (hapd->iconf->mbssid) {
+ for (j = 0; hapd->iconf->mbssid && j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hostapd_start_beacon(hapd, true)) {
+ for (;;) {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ if (j == 0)
+ break;
+ j--;
+ }
+ goto fail;
+ }
+ }
+ }
+
+ hapd = iface->bss[0];
+
+ hostapd_tx_queue_params(iface);
+
+ ap_list_init(iface);
+
+ hostapd_set_acl(hapd);
+
+ if (hostapd_driver_commit(hapd) < 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
+ "configuration", __func__);
+ goto fail;
+ }
+
+ /*
+ * WPS UPnP module can be initialized only when the "upnp_iface" is up.
+ * If "interface" and "upnp_iface" are the same (e.g., non-bridge
+ * mode), the interface is up only after driver_commit, so initialize
+ * WPS after driver_commit.
+ */
+ for (j = 0; j < iface->num_bss; j++) {
+ if (hostapd_init_wps_complete(iface->bss[j]))
+ goto fail;
+ }
+
+ if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ !res_dfs_offload) {
+ /*
+ * If freq is DFS, and DFS is offloaded to the driver, then wait
+ * for CAC to complete.
+ */
+ wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+ return res_dfs_offload;
+ }
+
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_FST
+ if (hapd->iconf->fst_cfg.group_id[0]) {
+ struct fst_wpa_obj iface_obj;
+
+ fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+ iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
+ &iface_obj, &hapd->iconf->fst_cfg);
+ if (!iface->fst) {
+ wpa_printf(MSG_ERROR, "Could not attach to FST %s",
+ hapd->iconf->fst_cfg.group_id);
+ goto fail;
+ }
+ }
+#endif /* CONFIG_FST */
+
+ hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+ hostapd_owe_update_trans(iface);
+ airtime_policy_update_init(iface);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+
+#ifdef CONFIG_MESH
+ if (delay_apply_cfg && !iface->mconf) {
+ wpa_printf(MSG_ERROR, "Error while completing mesh init");
+ goto fail;
+ }
+#endif /* CONFIG_MESH */
+
+ wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ iface->bss[0]->conf->iface);
+ if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+ iface->interfaces->terminate_on_error--;
+
+ for (j = 0; j < iface->num_bss; j++)
+ hostapd_neighbor_set_own_report(iface->bss[j]);
+
+ if (iface->interfaces && iface->interfaces->count > 1)
+ ieee802_11_set_beacons(iface);
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
+ hostapd_ubus_free_iface(iface);
+
+ if (iface->is_no_ir) {
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ return 0;
+ }
+
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_FST
+ if (iface->fst) {
+ fst_detach(iface->fst);
+ iface->fst = NULL;
+ }
+#endif /* CONFIG_FST */
+
+ if (iface->interfaces && iface->interfaces->terminate_on_error) {
+ eloop_terminate();
+ } else if (hapd->setup_complete_cb) {
+ /*
+ * Calling hapd->setup_complete_cb directly may cause iface
+ * deinitialization which may be accessed later by the caller.
+ */
+ eloop_register_timeout(0, 0,
+ hostapd_interface_setup_failure_handler,
+ iface, NULL);
+ }
+
+ return -1;
+}
+
+
+/**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+ struct hapd_interfaces *interfaces = iface->interfaces;
+ struct hostapd_data *hapd = iface->bss[0];
+ unsigned int i;
+ int not_ready_in_sync_ifaces = 0;
+
+ if (!iface->need_to_start_in_sync)
+ return hostapd_setup_interface_complete_sync(iface, err);
+
+ if (err) {
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
+ iface->need_to_start_in_sync = 0;
+
+ if (iface->is_no_ir) {
+ hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+ return 0;
+ }
+
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (interfaces && interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
+ }
+
+ if (iface->ready_to_start_in_sync) {
+ /* Already in ready and waiting. should never happpen */
+ return 0;
+ }
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (interfaces->iface[i]->need_to_start_in_sync &&
+ !interfaces->iface[i]->ready_to_start_in_sync)
+ not_ready_in_sync_ifaces++;
+ }
+
+ /*
+ * Check if this is the last interface, if yes then start all the other
+ * waiting interfaces. If not, add this interface to the waiting list.
+ */
+ if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
+ /*
+ * If this interface went through CAC, do not synchronize, just
+ * start immediately.
+ */
+ iface->need_to_start_in_sync = 0;
+ wpa_printf(MSG_INFO,
+ "%s: Finished CAC - bypass sync and start interface",
+ iface->bss[0]->conf->iface);
+ return hostapd_setup_interface_complete_sync(iface, err);
+ }
+
+ if (not_ready_in_sync_ifaces > 1) {
+ /* need to wait as there are other interfaces still coming up */
+ iface->ready_to_start_in_sync = 1;
+ wpa_printf(MSG_INFO,
+ "%s: Interface waiting to sync with other interfaces",
+ iface->bss[0]->conf->iface);
+ return 0;
+ }
+
+ wpa_printf(MSG_INFO,
+ "%s: Last interface to sync - starting all interfaces",
+ iface->bss[0]->conf->iface);
+ iface->need_to_start_in_sync = 0;
+ hostapd_setup_interface_complete_sync(iface, err);
+ for (i = 0; i < interfaces->count; i++) {
+ if (interfaces->iface[i]->need_to_start_in_sync &&
+ interfaces->iface[i]->ready_to_start_in_sync) {
+ hostapd_setup_interface_complete_sync(
+ interfaces->iface[i], 0);
+ /* Only once the interfaces are sync started */
+ interfaces->iface[i]->need_to_start_in_sync = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * hostapd_setup_interface - Setup of an interface
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initializes the driver interface, validates the configuration,
+ * and sets driver parameters based on the configuration.
+ * Flushes old stations, sets the channel, encryption,
+ * beacons, and WDS links based on the configuration.
+ *
+ * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS,
+ * or DFS operations, this function returns 0 before such operations have been
+ * completed. The pending operations are registered into eloop and will be
+ * completed from eloop callbacks. Those callbacks end up calling
+ * hostapd_setup_interface_complete() once setup has been completed.
+ */
+int hostapd_setup_interface(struct hostapd_iface *iface)
+{
+ int ret;
+
+ if (!iface->conf)
+ return -1;
+ ret = setup_interface(iface);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
+ iface->conf->bss[0]->iface);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * hostapd_alloc_bss_data - Allocate and initialize per-BSS data
+ * @hapd_iface: Pointer to interface data
+ * @conf: Pointer to per-interface configuration
+ * @bss: Pointer to per-BSS configuration for this BSS
+ * Returns: Pointer to allocated BSS data
+ *
+ * This function is used to allocate per-BSS data structure. This data will be
+ * freed after hostapd_cleanup() is called for it during interface
+ * deinitialization.
+ */
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf,
+ struct hostapd_bss_config *bss)
+{
+ struct hostapd_data *hapd;
+
+ hapd = os_zalloc(sizeof(*hapd));
+ if (hapd == NULL)
+ return NULL;
+
+ hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
+ hapd->iconf = conf;
+ hapd->conf = bss;
+ hapd->iface = hapd_iface;
+ if (bss && bss->config_id)
+ hapd->config_id = strdup(bss->config_id);
+ else
+ hapd->config_id = NULL;
+ if (conf)
+ hapd->driver = conf->driver;
+ hapd->ctrl_sock = -1;
+ dl_list_init(&hapd->ctrl_dst);
+ dl_list_init(&hapd->nr_db);
+ hapd->dhcp_sock = -1;
+#ifdef CONFIG_IEEE80211R_AP
+ dl_list_init(&hapd->l2_queue);
+ dl_list_init(&hapd->l2_oui_queue);
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+ dl_list_init(&hapd->sae_commit_queue);
+#endif /* CONFIG_SAE */
+
+ return hapd;
+}
+
+
+static void hostapd_bss_deinit(struct hostapd_data *hapd)
+{
+ if (!hapd)
+ return;
+ wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+ hapd->conf ? hapd->conf->iface : "N/A");
+ hostapd_bss_deinit_no_free(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_SQLITE
+ if (hapd->rad_attr_db) {
+ sqlite3_close(hapd->rad_attr_db);
+ hapd->rad_attr_db = NULL;
+ }
+#endif /* CONFIG_SQLITE */
+ hostapd_cleanup(hapd);
+}
+
+
+void hostapd_interface_deinit(struct hostapd_iface *iface)
+{
+ int j;
+
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ if (iface == NULL)
+ return;
+
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ iface->wait_channel_update = 0;
+ iface->is_no_ir = false;
+
+#ifdef CONFIG_FST
+ if (iface->fst) {
+ fst_detach(iface->fst);
+ iface->fst = NULL;
+ }
+#endif /* CONFIG_FST */
+
+ for (j = (int) iface->num_bss - 1; j >= 0; j--) {
+ if (!iface->bss)
+ break;
+ hostapd_bss_deinit(iface->bss[j]);
+ }
+
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+}
+
+
+void hostapd_interface_free(struct hostapd_iface *iface)
+{
+ size_t j;
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ for (j = 0; j < iface->num_bss; j++) {
+ if (!iface->bss)
+ break;
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p",
+ __func__, iface->bss[j]);
+ os_free(iface->bss[j]);
+ }
+ hostapd_cleanup_iface(iface);
+}
+
+
+struct hostapd_iface * hostapd_alloc_iface(void)
+{
+ struct hostapd_iface *hapd_iface;
+
+ hapd_iface = os_zalloc(sizeof(*hapd_iface));
+ if (!hapd_iface)
+ return NULL;
+
+ dl_list_init(&hapd_iface->sta_seen);
+
+ return hapd_iface;
+}
+
+
+/**
+ * hostapd_init - Allocate and initialize per-interface data
+ * @config_file: Path to the configuration file
+ * Returns: Pointer to the allocated interface data or %NULL on failure
+ *
+ * This function is used to allocate main data structures for per-interface
+ * data. The allocated data buffer will be freed by calling
+ * hostapd_cleanup_iface().
+ */
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ const char *config_file)
+{
+ struct hostapd_iface *hapd_iface = NULL;
+ struct hostapd_config *conf = NULL;
+ struct hostapd_data *hapd;
+ size_t i;
+
+ hapd_iface = hostapd_alloc_iface();
+ if (hapd_iface == NULL)
+ goto fail;
+
+ hapd_iface->config_fname = os_strdup(config_file);
+ if (hapd_iface->config_fname == NULL)
+ goto fail;
+
+ conf = interfaces->config_read_cb(hapd_iface->config_fname);
+ if (conf == NULL)
+ goto fail;
+ hapd_iface->conf = conf;
+
+ hapd_iface->num_bss = conf->num_bss;
+ hapd_iface->bss = os_calloc(conf->num_bss,
+ sizeof(struct hostapd_data *));
+ if (hapd_iface->bss == NULL)
+ goto fail;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ hapd = hapd_iface->bss[i] =
+ hostapd_alloc_bss_data(hapd_iface, conf,
+ conf->bss[i]);
+ if (hapd == NULL)
+ goto fail;
+ hapd->msg_ctx = hapd;
+ }
+
+ return hapd_iface;
+
+fail:
+ wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
+ config_file);
+ if (conf)
+ hostapd_config_free(conf);
+ if (hapd_iface) {
+ os_free(hapd_iface->config_fname);
+ os_free(hapd_iface->bss);
+ wpa_printf(MSG_DEBUG, "%s: free iface %p",
+ __func__, hapd_iface);
+ os_free(hapd_iface);
+ }
+ return NULL;
+}
+
+
+static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname)
+{
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_iface *iface = interfaces->iface[i];
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * hostapd_interface_init_bss - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a BSS. This BSS is
+ * added to an existing interface sharing the same radio (if any) or a new
+ * interface is created if this is the first interface on a radio. This
+ * allocate memory for the BSS. No actual driver operations are started.
+ *
+ * This is similar to hostapd_interface_init(), but for a case where the
+ * configuration is used to add a single BSS instead of all BSSes for a radio.
+ */
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug)
+{
+ struct hostapd_iface *new_iface = NULL, *iface = NULL;
+ struct hostapd_data *hapd;
+ int k;
+ size_t i, bss_idx;
+
+ if (!phy || !*phy)
+ return NULL;
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
+ iface = interfaces->iface[i];
+ break;
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
+ config_fname, phy, iface ? "" : " --> new PHY");
+ if (iface) {
+ struct hostapd_config *conf;
+ struct hostapd_bss_config **tmp_conf;
+ struct hostapd_data **tmp_bss;
+ struct hostapd_bss_config *bss;
+ const char *ifname;
+
+ /* Add new BSS to existing iface */
+ conf = interfaces->config_read_cb(config_fname);
+ if (conf == NULL)
+ return NULL;
+ if (conf->num_bss > 1) {
+ wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ ifname = conf->bss[0]->iface;
+ if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+ wpa_printf(MSG_ERROR,
+ "Interface name %s already in use", ifname);
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ tmp_conf = os_realloc_array(
+ iface->conf->bss, iface->conf->num_bss + 1,
+ sizeof(struct hostapd_bss_config *));
+ tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+ sizeof(struct hostapd_data *));
+ if (tmp_bss)
+ iface->bss = tmp_bss;
+ if (tmp_conf) {
+ iface->conf->bss = tmp_conf;
+ iface->conf->last_bss = tmp_conf[0];
+ }
+ if (tmp_bss == NULL || tmp_conf == NULL) {
+ hostapd_config_free(conf);
+ return NULL;
+ }
+ bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0];
+ iface->conf->num_bss++;
+
+ hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+ if (hapd == NULL) {
+ iface->conf->num_bss--;
+ hostapd_config_free(conf);
+ return NULL;
+ }
+ iface->conf->last_bss = bss;
+ iface->bss[iface->num_bss] = hapd;
+ hapd->msg_ctx = hapd;
+
+ bss_idx = iface->num_bss++;
+ conf->num_bss--;
+ conf->bss[0] = NULL;
+ hostapd_config_free(conf);
+ } else {
+ /* Add a new iface with the first BSS */
+ new_iface = iface = hostapd_init(interfaces, config_fname);
+ if (!iface)
+ return NULL;
+ os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+ iface->interfaces = interfaces;
+ bss_idx = 0;
+ }
+
+ for (k = 0; k < debug; k++) {
+ if (iface->bss[bss_idx]->conf->logger_stdout_level > 0)
+ iface->bss[bss_idx]->conf->logger_stdout_level--;
+ }
+
+ if (iface->conf->bss[bss_idx]->iface[0] == '\0' &&
+ !hostapd_drv_none(iface->bss[bss_idx])) {
+ wpa_printf(MSG_ERROR, "Interface name not specified in %s",
+ config_fname);
+ if (new_iface)
+ hostapd_interface_deinit_free(new_iface);
+ return NULL;
+ }
+
+ return iface;
+}
+
+
+void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+{
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ if (iface == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u",
+ __func__, (unsigned int) iface->num_bss,
+ (unsigned int) iface->conf->num_bss);
+ driver = iface->bss[0]->driver;
+ drv_priv = iface->bss[0]->drv_priv;
+ hostapd_ubus_free_iface(iface);
+ hostapd_interface_deinit(iface);
+ wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ __func__, driver, drv_priv);
+ if (driver && driver->hapd_deinit && drv_priv) {
+ if (!iface->bss[0]->mld_first_bss)
+ driver->hapd_deinit(drv_priv);
+ hostapd_clear_drv_priv(iface->bss[0]);
+ }
+ hostapd_interface_free(iface);
+}
+
+
+static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv,
+ struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ __func__, driver, drv_priv);
+ if (driver && driver->hapd_deinit && drv_priv) {
+ if (!hapd_iface->bss[0]->mld_first_bss)
+ driver->hapd_deinit(drv_priv);
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
+ __func__, (int) j,
+ hapd_iface->bss[j]->drv_priv);
+ if (hapd_iface->bss[j]->drv_priv == drv_priv) {
+ hostapd_clear_drv_priv(hapd_iface->bss[j]);
+ hapd_iface->extended_capa = NULL;
+ hapd_iface->extended_capa_mask = NULL;
+ hapd_iface->extended_capa_len = 0;
+ }
+ }
+ }
+}
+
+
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ if (!hapd_iface)
+ return -1;
+
+ if (hapd_iface->enable_iface_cb)
+ return hapd_iface->enable_iface_cb(hapd_iface);
+
+ if (hapd_iface->bss[0]->drv_priv != NULL) {
+ wpa_printf(MSG_ERROR, "Interface %s already enabled",
+ hapd_iface->conf->bss[0]->iface);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Enable interface %s",
+ hapd_iface->conf->bss[0]->iface);
+
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+ if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+ wpa_printf(MSG_INFO, "Invalid configuration - cannot enable");
+ return -1;
+ }
+
+ if (hapd_iface->interfaces == NULL ||
+ hapd_iface->interfaces->driver_init == NULL ||
+ hapd_iface->interfaces->driver_init(hapd_iface))
+ return -1;
+
+ if (hostapd_setup_interface(hapd_iface)) {
+ hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ wpa_printf(MSG_DEBUG, "Reload interface %s",
+ hapd_iface->conf->bss[0]->iface);
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+ if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+ wpa_printf(MSG_ERROR, "Updated configuration is invalid");
+ return -1;
+ }
+ hostapd_clear_old(hapd_iface);
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_reload_bss(hapd_iface->bss[j]);
+
+ return 0;
+}
+
+
+int hostapd_reload_bss_only(struct hostapd_data *bss)
+{
+
+ wpa_printf(MSG_DEBUG, "Reload BSS %s", bss->conf->iface);
+ hostapd_set_security_params(bss->conf, 1);
+ if (hostapd_config_check(bss->iconf, 1) < 0) {
+ wpa_printf(MSG_ERROR, "Updated BSS configuration is invalid");
+ return -1;
+ }
+ hostapd_clear_old_bss(bss);
+ hostapd_reload_bss(bss);
+ return 0;
+}
+
+
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+
+ if (hapd_iface == NULL)
+ return -1;
+
+ if (hapd_iface->disable_iface_cb)
+ return hapd_iface->disable_iface_cb(hapd_iface);
+
+ if (hapd_iface->bss[0]->drv_priv == NULL) {
+ wpa_printf(MSG_INFO, "Interface %s already disabled",
+ hapd_iface->conf->bss[0]->iface);
+ return -1;
+ }
+
+ wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ driver = hapd_iface->bss[0]->driver;
+ drv_priv = hapd_iface->bss[0]->drv_priv;
+
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+#ifdef NEED_AP_MLME
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_cleanup_cs_params(hapd_iface->bss[j]);
+#endif /* NEED_AP_MLME */
+
+ /* same as hostapd_interface_deinit without deinitializing ctrl-iface */
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ struct hostapd_data *hapd = hapd_iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ }
+
+ hostapd_deinit_driver(driver, drv_priv, hapd_iface);
+
+ /* From hostapd_cleanup_iface: These were initialized in
+ * hostapd_setup_interface and hostapd_setup_interface_complete
+ */
+ hostapd_cleanup_iface_partial(hapd_iface);
+
+ wpa_printf(MSG_DEBUG, "Interface %s disabled",
+ hapd_iface->bss[0]->conf->iface);
+ hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
+ return 0;
+}
+
+
+static struct hostapd_iface *
+hostapd_iface_alloc(struct hapd_interfaces *interfaces)
+{
+ struct hostapd_iface **iface, *hapd_iface;
+
+ iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
+ sizeof(struct hostapd_iface *));
+ if (iface == NULL)
+ return NULL;
+ interfaces->iface = iface;
+ hapd_iface = interfaces->iface[interfaces->count] =
+ hostapd_alloc_iface();
+ if (hapd_iface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+ "the interface", __func__);
+ return NULL;
+ }
+ interfaces->count++;
+ hapd_iface->interfaces = interfaces;
+
+ return hapd_iface;
+}
+
+
+static struct hostapd_config *
+hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
+ const char *ctrl_iface, const char *driver)
+{
+ struct hostapd_bss_config *bss;
+ struct hostapd_config *conf;
+
+ /* Allocates memory for bss and conf */
+ conf = hostapd_config_defaults();
+ if (conf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+ "configuration", __func__);
+ return NULL;
+ }
+
+ if (driver) {
+ int j;
+
+ for (j = 0; wpa_drivers[j]; j++) {
+ if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
+ conf->driver = wpa_drivers[j];
+ goto skip;
+ }
+ }
+
+ wpa_printf(MSG_ERROR,
+ "Invalid/unknown driver '%s' - registering the default driver",
+ driver);
+ }
+
+ conf->driver = wpa_drivers[0];
+ if (conf->driver == NULL) {
+ wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+skip:
+ bss = conf->last_bss = conf->bss[0];
+
+ os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
+ bss->ctrl_interface = os_strdup(ctrl_iface);
+ if (bss->ctrl_interface == NULL) {
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ /* Reading configuration file skipped, will be done in SET!
+ * From reading the configuration till the end has to be done in
+ * SET
+ */
+ return conf;
+}
+
+
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf)
+{
+ size_t i;
+ struct hostapd_data *hapd;
+
+ hapd_iface->bss = os_calloc(conf->num_bss,
+ sizeof(struct hostapd_data *));
+ if (hapd_iface->bss == NULL)
+ return -1;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ hapd = hapd_iface->bss[i] =
+ hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
+ if (hapd == NULL) {
+ while (i > 0) {
+ i--;
+ os_free(hapd_iface->bss[i]);
+ hapd_iface->bss[i] = NULL;
+ }
+ os_free(hapd_iface->bss);
+ hapd_iface->bss = NULL;
+ return -1;
+ }
+ hapd->msg_ctx = hapd;
+ }
+
+ hapd_iface->conf = conf;
+ hapd_iface->num_bss = conf->num_bss;
+
+ return 0;
+}
+
+
+int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+ struct hostapd_config *conf = NULL;
+ struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+ struct hostapd_data *hapd;
+ char *ptr;
+ size_t i, j;
+ const char *conf_file = NULL, *phy_name = NULL;
+
+ if (os_strncmp(buf, "bss_config=", 11) == 0) {
+ char *pos;
+ phy_name = buf + 11;
+ pos = os_strchr(phy_name, ':');
+ if (!pos)
+ return -1;
+ *pos++ = '\0';
+ conf_file = pos;
+ if (!os_strlen(conf_file))
+ return -1;
+
+ hapd_iface = hostapd_interface_init_bss(interfaces, phy_name,
+ conf_file, 0);
+ if (!hapd_iface)
+ return -1;
+ for (j = 0; j < interfaces->count; j++) {
+ if (interfaces->iface[j] == hapd_iface)
+ break;
+ }
+ if (j == interfaces->count) {
+ struct hostapd_iface **tmp;
+ tmp = os_realloc_array(interfaces->iface,
+ interfaces->count + 1,
+ sizeof(struct hostapd_iface *));
+ if (!tmp) {
+ hostapd_interface_deinit_free(hapd_iface);
+ return -1;
+ }
+ interfaces->iface = tmp;
+ interfaces->iface[interfaces->count++] = hapd_iface;
+ new_iface = hapd_iface;
+ }
+
+ if (new_iface) {
+ if (interfaces->driver_init(hapd_iface))
+ goto fail;
+
+ if (hostapd_setup_interface(hapd_iface)) {
+ hostapd_deinit_driver(
+ hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
+ goto fail;
+ }
+ } else {
+ /* Assign new BSS with bss[0]'s driver info */
+ hapd = hapd_iface->bss[hapd_iface->num_bss - 1];
+ hapd->driver = hapd_iface->bss[0]->driver;
+ hapd->drv_priv = hapd_iface->bss[0]->drv_priv;
+ os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr,
+ ETH_ALEN);
+
+ if (start_ctrl_iface_bss(hapd) < 0 ||
+ (hapd_iface->state == HAPD_IFACE_ENABLED &&
+ hostapd_setup_bss(hapd, -1, true))) {
+ hostapd_cleanup(hapd);
+ hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+ hapd_iface->conf->num_bss--;
+ hapd_iface->num_bss--;
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
+ __func__, hapd, hapd->conf->iface);
+ hostapd_config_free_bss(hapd->conf);
+ hapd->conf = NULL;
+ os_free(hapd);
+ return -1;
+ }
+ }
+ hostapd_owe_update_trans(hapd_iface);
+ return 0;
+ }
+
+ ptr = os_strchr(buf, ' ');
+ if (ptr == NULL)
+ return -1;
+ *ptr++ = '\0';
+
+ if (os_strncmp(ptr, "config=", 7) == 0)
+ conf_file = ptr + 7;
+
+ for (i = 0; i < interfaces->count; i++) {
+ bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+ mld_ap = interfaces->iface[i]->conf->bss[0]->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
+ buf) && !mld_ap) {
+ wpa_printf(MSG_INFO, "Cannot add interface - it "
+ "already exists");
+ return -1;
+ }
+ }
+
+ hapd_iface = hostapd_iface_alloc(interfaces);
+ if (hapd_iface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+ "for interface", __func__);
+ goto fail;
+ }
+ new_iface = hapd_iface;
+
+ if (conf_file && interfaces->config_read_cb) {
+ conf = interfaces->config_read_cb(conf_file);
+ if (conf && conf->bss)
+ os_strlcpy(conf->bss[0]->iface, buf,
+ sizeof(conf->bss[0]->iface));
+ } else {
+ char *driver = os_strchr(ptr, ' ');
+
+ if (driver)
+ *driver++ = '\0';
+ conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
+ }
+
+ if (conf == NULL || conf->bss == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+ "for configuration", __func__);
+ goto fail;
+ }
+
+ if (hostapd_data_alloc(hapd_iface, conf) < 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+ "for hostapd", __func__);
+ goto fail;
+ }
+ conf = NULL;
+
+ if (start_ctrl_iface(hapd_iface) < 0)
+ goto fail;
+
+ wpa_printf(MSG_INFO, "Add interface '%s'",
+ hapd_iface->conf->bss[0]->iface);
+
+ return 0;
+
+fail:
+ if (conf)
+ hostapd_config_free(conf);
+ if (hapd_iface) {
+ if (hapd_iface->bss) {
+ for (i = 0; i < hapd_iface->num_bss; i++) {
+ hapd = hapd_iface->bss[i];
+ if (!hapd)
+ continue;
+ if (hapd_iface->interfaces &&
+ hapd_iface->interfaces->ctrl_iface_deinit)
+ hapd_iface->interfaces->
+ ctrl_iface_deinit(hapd);
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+ __func__, hapd_iface->bss[i],
+ hapd->conf->iface);
+ hostapd_cleanup(hapd);
+ os_free(hapd);
+ hapd_iface->bss[i] = NULL;
+ }
+ os_free(hapd_iface->bss);
+ hapd_iface->bss = NULL;
+ }
+ if (new_iface) {
+ interfaces->count--;
+ interfaces->iface[interfaces->count] = NULL;
+ }
+ hostapd_cleanup_iface(hapd_iface);
+ }
+ return -1;
+}
+
+
+static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
+{
+ size_t i;
+
+ wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface);
+
+ /* Remove hostapd_data only if it has already been initialized */
+ if (idx < iface->num_bss) {
+ struct hostapd_data *hapd = iface->bss[idx];
+
+ hostapd_bss_deinit(hapd);
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+ __func__, hapd, hapd->conf->iface);
+ hostapd_config_free_bss(hapd->conf);
+ hapd->conf = NULL;
+ os_free(hapd);
+
+ iface->num_bss--;
+
+ for (i = idx; i < iface->num_bss; i++)
+ iface->bss[i] = iface->bss[i + 1];
+ } else {
+ hostapd_config_free_bss(iface->conf->bss[idx]);
+ iface->conf->bss[idx] = NULL;
+ }
+
+ iface->conf->num_bss--;
+ for (i = idx; i < iface->conf->num_bss; i++)
+ iface->conf->bss[i] = iface->conf->bss[i + 1];
+
+ return 0;
+}
+
+
+int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+ struct hostapd_iface *hapd_iface;
+ size_t i, j, k = 0;
+
+ for (i = 0; i < interfaces->count; i++) {
+ hapd_iface = interfaces->iface[i];
+ if (hapd_iface == NULL)
+ return -1;
+ if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
+ wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+ hostapd_interface_deinit_free(hapd_iface);
+ k = i;
+ while (k < (interfaces->count - 1)) {
+ interfaces->iface[k] =
+ interfaces->iface[k + 1];
+ k++;
+ }
+ interfaces->count--;
+ return 0;
+ }
+
+ for (j = 0; j < hapd_iface->conf->num_bss; j++) {
+ if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+ hapd_iface->driver_ap_teardown =
+ !(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+ return hostapd_remove_bss(hapd_iface, j);
+ }
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * hostapd_new_assoc_sta - Notify that a new station associated with the AP
+ * @hapd: Pointer to BSS data
+ * @sta: Pointer to the associated STA data
+ * @reassoc: 1 to indicate this was a re-association; 0 = first association
+ *
+ * This function will be called whenever a station associates with the AP. It
+ * can be called from ieee802_11.c for drivers that export MLME to hostapd and
+ * from drv_callbacks.c based on driver events for drivers that take care of
+ * management frames (IEEE 802.11 authentication and association) internally.
+ */
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc)
+{
+ if (hapd->tkip_countermeasures) {
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ return;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return;
+#endif /* CONFIG_IEEE80211BE */
+
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ sta->post_csa_sa_query = 0;
+
+#ifdef CONFIG_P2P
+ if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
+ sta->no_p2p_set = 1;
+ hapd->num_sta_no_p2p++;
+ if (hapd->num_sta_no_p2p == 1)
+ hostapd_p2p_non_p2p_sta_connected(hapd);
+ }
+#endif /* CONFIG_P2P */
+
+ airtime_policy_new_sta(hapd, sta);
+
+ /* Start accounting here, if IEEE 802.1X and WPA are not used.
+ * IEEE 802.1X/WPA code will start accounting after the station has
+ * been authorized. */
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+ ap_sta_set_authorized(hapd, sta, 1);
+ os_get_reltime(&sta->connected_time);
+ accounting_sta_start(hapd, sta);
+ }
+
+ /* Start IEEE 802.1X authentication process for new stations */
+ ieee802_1x_new_station(hapd, sta);
+ if (reassoc) {
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
+ wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
+ } else
+ wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
+ if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: %s: canceled wired ap_handle_timer timeout for "
+ MACSTR,
+ hapd->conf->iface, __func__,
+ MAC2STR(sta->addr));
+ }
+ } else if (!(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: %s: reschedule ap_handle_timer timeout for "
+ MACSTR " (%d seconds - ap_max_inactivity)",
+ hapd->conf->iface, __func__, MAC2STR(sta->addr),
+ hapd->conf->ap_max_inactivity);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ ap_handle_timer, hapd, sta);
+ }
+
+#ifdef CONFIG_MACSEC
+ if (hapd->conf->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
+ hapd->conf->mka_psk_set)
+ ieee802_1x_create_preshared_mka_hapd(hapd, sta);
+ else
+ ieee802_1x_alloc_kay_sm_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
+}
+
+
+const char * hostapd_state_text(enum hostapd_iface_state s)
+{
+ switch (s) {
+ case HAPD_IFACE_UNINITIALIZED:
+ return "UNINITIALIZED";
+ case HAPD_IFACE_DISABLED:
+ return "DISABLED";
+ case HAPD_IFACE_COUNTRY_UPDATE:
+ return "COUNTRY_UPDATE";
+ case HAPD_IFACE_ACS:
+ return "ACS";
+ case HAPD_IFACE_HT_SCAN:
+ return "HT_SCAN";
+ case HAPD_IFACE_DFS:
+ return "DFS";
+ case HAPD_IFACE_ENABLED:
+ return "ENABLED";
+ case HAPD_IFACE_NO_IR:
+ return "NO_IR";
+ }
+
+ return "UNKNOWN";
+}
+
+
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
+{
+ wpa_printf(MSG_INFO, "%s: interface state %s->%s",
+ iface->conf ? iface->conf->bss[0]->iface : "N/A",
+ hostapd_state_text(iface->state), hostapd_state_text(s));
+ iface->state = s;
+}
+
+
+int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ for (i = 0; i < iface->num_bss; i++)
+ if (iface->bss[i]->csa_in_progress)
+ return 1;
+ return 0;
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void free_beacon_data(struct beacon_data *beacon)
+{
+ os_free(beacon->head);
+ beacon->head = NULL;
+ os_free(beacon->tail);
+ beacon->tail = NULL;
+ os_free(beacon->probe_resp);
+ beacon->probe_resp = NULL;
+ os_free(beacon->beacon_ies);
+ beacon->beacon_ies = NULL;
+ os_free(beacon->proberesp_ies);
+ beacon->proberesp_ies = NULL;
+ os_free(beacon->assocresp_ies);
+ beacon->assocresp_ies = NULL;
+}
+
+
+static int hostapd_build_beacon_data(struct hostapd_data *hapd,
+ struct beacon_data *beacon)
+{
+ struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
+ struct wpa_driver_ap_params params;
+ int ret;
+
+ os_memset(beacon, 0, sizeof(*beacon));
+ ret = ieee802_11_build_ap_params(hapd, ¶ms);
+ if (ret < 0)
+ return ret;
+
+ ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
+ &proberesp_extra,
+ &assocresp_extra);
+ if (ret)
+ goto free_ap_params;
+
+ ret = -1;
+ beacon->head = os_memdup(params.head, params.head_len);
+ if (!beacon->head)
+ goto free_ap_extra_ies;
+
+ beacon->head_len = params.head_len;
+
+ beacon->tail = os_memdup(params.tail, params.tail_len);
+ if (!beacon->tail)
+ goto free_beacon;
+
+ beacon->tail_len = params.tail_len;
+
+ if (params.proberesp != NULL) {
+ beacon->probe_resp = os_memdup(params.proberesp,
+ params.proberesp_len);
+ if (!beacon->probe_resp)
+ goto free_beacon;
+
+ beacon->probe_resp_len = params.proberesp_len;
+ }
+
+ /* copy the extra ies */
+ if (beacon_extra) {
+ beacon->beacon_ies = os_memdup(beacon_extra->buf,
+ wpabuf_len(beacon_extra));
+ if (!beacon->beacon_ies)
+ goto free_beacon;
+
+ beacon->beacon_ies_len = wpabuf_len(beacon_extra);
+ }
+
+ if (proberesp_extra) {
+ beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
+ wpabuf_len(proberesp_extra));
+ if (!beacon->proberesp_ies)
+ goto free_beacon;
+
+ beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
+ }
+
+ if (assocresp_extra) {
+ beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
+ wpabuf_len(assocresp_extra));
+ if (!beacon->assocresp_ies)
+ goto free_beacon;
+
+ beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
+ }
+
+ ret = 0;
+free_beacon:
+ /* if the function fails, the caller should not free beacon data */
+ if (ret)
+ free_beacon_data(beacon);
+
+free_ap_extra_ies:
+ hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
+ assocresp_extra);
+free_ap_params:
+ ieee802_11_free_ap_params(¶ms);
+ return ret;
+}
+
+
+/*
+ * TODO: This flow currently supports only changing channel and width within
+ * the same hw_mode. Any other changes to MAC parameters or provided settings
+ * are not supported.
+ */
+static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ struct hostapd_config *conf,
+ struct hostapd_freq_params *params,
+ struct hostapd_freq_params *old_params)
+{
+ int channel;
+ u8 seg0 = 0, seg1 = 0;
+ struct hostapd_hw_modes *mode;
+
+ if (!params->channel) {
+ /* check if the new channel is supported by hw */
+ params->channel = hostapd_hw_get_channel(hapd, params->freq);
+ }
+
+ channel = params->channel;
+ if (!channel)
+ return -1;
+
+ hostapd_determine_mode(hapd->iface);
+ mode = hapd->iface->current_mode;
+
+ /* if a pointer to old_params is provided we save previous state */
+ if (old_params &&
+ hostapd_set_freq_params(old_params, conf->hw_mode,
+ hostapd_hw_get_freq(hapd, conf->channel),
+ conf->channel, conf->enable_edmg,
+ conf->edmg_channel, conf->ieee80211n,
+ conf->ieee80211ac, conf->ieee80211ax,
+ conf->ieee80211be, conf->secondary_channel,
+ hostapd_get_oper_chwidth(conf),
+ hostapd_get_oper_centr_freq_seg0_idx(conf),
+ hostapd_get_oper_centr_freq_seg1_idx(conf),
+ conf->vht_capab,
+ mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+ NULL,
+ mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+ NULL))
+ return -1;
+
+ switch (params->bandwidth) {
+ case 0:
+ case 20:
+ conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ break;
+ case 40:
+ case 80:
+ case 160:
+ case 320:
+ conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ break;
+ default:
+ return -1;
+ }
+
+ switch (params->bandwidth) {
+ case 0:
+ case 20:
+ case 40:
+ hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_USE_HT);
+ break;
+ case 80:
+ if (params->center_freq2)
+ hostapd_set_oper_chwidth(conf,
+ CONF_OPER_CHWIDTH_80P80MHZ);
+ else
+ hostapd_set_oper_chwidth(conf,
+ CONF_OPER_CHWIDTH_80MHZ);
+ break;
+ case 160:
+ hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_160MHZ);
+ break;
+ case 320:
+ hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_320MHZ);
+ break;
+ default:
+ return -1;
+ }
+
+ conf->channel = channel;
+ conf->ieee80211n = params->ht_enabled;
+ conf->ieee80211ac = params->vht_enabled;
+ conf->secondary_channel = params->sec_channel_offset;
+ ieee80211_freq_to_chan(params->center_freq1,
+ &seg0);
+ ieee80211_freq_to_chan(params->center_freq2,
+ &seg1);
+ hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
+ hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
+
+ /* TODO: maybe call here hostapd_config_check here? */
+
+ return 0;
+}
+
+
+static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_freq_params old_freq;
+ int ret;
+#ifdef CONFIG_IEEE80211BE
+ u16 old_punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+ u8 chan, bandwidth;
+
+ os_memset(&old_freq, 0, sizeof(old_freq));
+ if (!iface || !iface->freq || hapd->csa_in_progress)
+ return -1;
+
+ switch (settings->freq_params.bandwidth) {
+ case 80:
+ if (settings->freq_params.center_freq2)
+ bandwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+ else
+ bandwidth = CONF_OPER_CHWIDTH_80MHZ;
+ break;
+ case 160:
+ bandwidth = CONF_OPER_CHWIDTH_160MHZ;
+ break;
+ case 320:
+ bandwidth = CONF_OPER_CHWIDTH_320MHZ;
+ break;
+ default:
+ bandwidth = CONF_OPER_CHWIDTH_USE_HT;
+ break;
+ }
+
+ if (ieee80211_freq_to_channel_ext(
+ settings->freq_params.freq,
+ settings->freq_params.sec_channel_offset,
+ bandwidth,
+ &hapd->iface->cs_oper_class,
+ &chan) == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG,
+ "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d)",
+ settings->freq_params.freq,
+ settings->freq_params.sec_channel_offset,
+ settings->freq_params.vht_enabled,
+ settings->freq_params.he_enabled,
+ settings->freq_params.eht_enabled);
+ return -1;
+ }
+
+ settings->freq_params.channel = chan;
+
+ ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &settings->freq_params,
+ &old_freq);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_IEEE80211BE
+ old_punct_bitmap = iface->conf->punct_bitmap;
+ iface->conf->punct_bitmap = settings->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+
+ /* change back the configuration */
+#ifdef CONFIG_IEEE80211BE
+ iface->conf->punct_bitmap = old_punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+ hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &old_freq, NULL);
+
+ if (ret)
+ return ret;
+
+ /* set channel switch parameters for csa ie */
+ hapd->cs_freq_params = settings->freq_params;
+ hapd->cs_count = settings->cs_count;
+ hapd->cs_block_tx = settings->block_tx;
+
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+ if (ret) {
+ free_beacon_data(&settings->beacon_after);
+ return ret;
+ }
+
+ settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+ settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+ settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+ settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+
+ return 0;
+}
+
+
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
+{
+ os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+ hapd->cs_count = 0;
+ hapd->cs_block_tx = 0;
+ hapd->cs_c_off_beacon = 0;
+ hapd->cs_c_off_proberesp = 0;
+ hapd->csa_in_progress = 0;
+ hapd->cs_c_off_ecsa_beacon = 0;
+ hapd->cs_c_off_ecsa_proberesp = 0;
+}
+
+
+void hostapd_chan_switch_config(struct hostapd_data *hapd,
+ struct hostapd_freq_params *freq_params)
+{
+ if (freq_params->eht_enabled)
+ hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_ENABLED;
+ else
+ hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_DISABLED;
+
+ if (freq_params->he_enabled)
+ hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED;
+ else
+ hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_DISABLED;
+
+ if (freq_params->vht_enabled)
+ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
+ else
+ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "CHAN_SWITCH EHT config 0x%x HE config 0x%x VHT config 0x%x",
+ hapd->iconf->ch_switch_eht_config,
+ hapd->iconf->ch_switch_he_config,
+ hapd->iconf->ch_switch_vht_config);
+}
+
+
+int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ int ret;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+ wpa_printf(MSG_INFO, "CSA is not supported");
+ return -1;
+ }
+
+ ret = hostapd_fill_csa_settings(hapd, settings);
+ if (ret)
+ return ret;
+
+ ret = hostapd_drv_switch_channel(hapd, settings);
+ free_beacon_data(&settings->beacon_csa);
+ free_beacon_data(&settings->beacon_after);
+
+ if (ret) {
+ /* if we failed, clean cs parameters */
+ hostapd_cleanup_cs_params(hapd);
+ return ret;
+ }
+
+ hapd->csa_in_progress = 1;
+ return 0;
+}
+
+
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params)
+{
+ int seg0_idx = 0, seg1_idx = 0;
+ enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT;
+
+ wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+ if (freq_params->center_freq1)
+ seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+ if (freq_params->center_freq2)
+ seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+
+ switch (freq_params->bandwidth) {
+ case 0:
+ case 20:
+ case 40:
+ bw = CONF_OPER_CHWIDTH_USE_HT;
+ break;
+ case 80:
+ if (freq_params->center_freq2)
+ bw = CONF_OPER_CHWIDTH_80P80MHZ;
+ else
+ bw = CONF_OPER_CHWIDTH_80MHZ;
+ break;
+ case 160:
+ bw = CONF_OPER_CHWIDTH_160MHZ;
+ break;
+ case 320:
+ bw = CONF_OPER_CHWIDTH_320MHZ;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+ freq_params->bandwidth);
+ break;
+ }
+
+ iface->freq = freq_params->freq;
+ iface->conf->channel = freq_params->channel;
+ iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
+ hostapd_set_oper_chwidth(iface->conf, bw);
+ iface->conf->ieee80211n = freq_params->ht_enabled;
+ iface->conf->ieee80211ac = freq_params->vht_enabled;
+ iface->conf->ieee80211ax = freq_params->he_enabled;
+ iface->conf->ieee80211be = freq_params->eht_enabled;
+
+ /*
+ * cs_params must not be cleared earlier because the freq_params
+ * argument may actually point to one of these.
+ * These params will be cleared during interface disable below.
+ */
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd)
+{
+ hapd->cca_count = 0;
+ hapd->cca_color = 0;
+ hapd->cca_c_off_beacon = 0;
+ hapd->cca_c_off_proberesp = 0;
+ hapd->cca_in_progress = false;
+}
+
+
+static int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ struct cca_settings *settings)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ u8 old_color;
+ int ret;
+
+ if (!iface || iface->conf->he_op.he_bss_color_disabled)
+ return -1;
+
+ old_color = iface->conf->he_op.he_bss_color;
+ iface->conf->he_op.he_bss_color = hapd->cca_color;
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+ if (ret)
+ return ret;
+
+ iface->conf->he_op.he_bss_color = old_color;
+
+ settings->cca_count = hapd->cca_count;
+ settings->cca_color = hapd->cca_color,
+ hapd->cca_in_progress = true;
+
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_cca);
+ if (ret) {
+ free_beacon_data(&settings->beacon_after);
+ return ret;
+ }
+
+ settings->counter_offset_beacon = hapd->cca_c_off_beacon;
+ settings->counter_offset_presp = hapd->cca_c_off_proberesp;
+
+ return 0;
+}
+
+
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+ os_time_t delta_t;
+ unsigned int b;
+ int i, r;
+
+ /* CCA can be triggered once the handler constantly receives
+ * color collision events to for at least
+ * DOT11BSS_COLOR_COLLISION_AP_PERIOD (50 s by default). */
+ delta_t = hapd->last_color_collision.sec -
+ hapd->first_color_collision.sec;
+ if (delta_t < DOT11BSS_COLOR_COLLISION_AP_PERIOD)
+ return;
+
+ r = os_random() % HE_OPERATION_BSS_COLOR_MAX;
+ for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
+ if (r && !(hapd->color_collision_bitmap & (1ULL << r)))
+ break;
+
+ r = (r + 1) % HE_OPERATION_BSS_COLOR_MAX;
+ }
+
+ if (i == HE_OPERATION_BSS_COLOR_MAX) {
+ /* There are no free colors so turn BSS coloring off */
+ wpa_printf(MSG_INFO,
+ "No free colors left, turning off BSS coloring");
+ hapd->iface->conf->he_op.he_bss_color_disabled = 1;
+ hapd->iface->conf->he_op.he_bss_color = os_random() % 63 + 1;
+ for (b = 0; b < hapd->iface->num_bss; b++)
+ ieee802_11_set_beacon(hapd->iface->bss[b]);
+ return;
+ }
+
+ for (b = 0; b < hapd->iface->num_bss; b++) {
+ struct hostapd_data *bss = hapd->iface->bss[b];
+ struct cca_settings settings;
+ int ret;
+
+ hostapd_cleanup_cca_params(bss);
+ bss->cca_color = r;
+ bss->cca_count = 10;
+
+ if (hostapd_fill_cca_settings(bss, &settings)) {
+ hostapd_cleanup_cca_params(bss);
+ continue;
+ }
+
+ ret = hostapd_drv_switch_color(bss, &settings);
+ if (ret)
+ hostapd_cleanup_cca_params(bss);
+
+ free_beacon_data(&settings.beacon_cca);
+ free_beacon_data(&settings.beacon_after);
+ }
+}
+
+
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+{
+ struct os_reltime now;
+
+ if (hapd->cca_in_progress)
+ return;
+
+ if (os_get_reltime(&now))
+ return;
+
+ hapd->color_collision_bitmap = bitmap;
+ hapd->last_color_collision = now;
+
+ if (eloop_is_timeout_registered(hostapd_switch_color_timeout_handler,
+ hapd, NULL))
+ return;
+
+ hapd->first_color_collision = now;
+ /* 10 s window as margin for persistent color collision reporting */
+ eloop_register_timeout(DOT11BSS_COLOR_COLLISION_AP_PERIOD + 10, 0,
+ hostapd_switch_color_timeout_handler,
+ hapd, NULL);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+#endif /* NEED_AP_MLME */
+
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname)
+{
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_iface *iface = interfaces->iface[i];
+
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ return hapd;
+ }
+ }
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_BSS_TRANS_IMPL
+// Periodic check of STA info and trigger BSS transition if necessary
+static void hostapd_sta_bss_transition_check(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ char shellBuf[100];
+ char *s;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next)
+ {
+ if (sta)
+ {
+ s = shellBuf;
+ s += sprintf(s, "./etc/log/bss_tm_trigger.sh ");
+ s += sprintf(s, "%s " MACSTR, hapd->iface->phy, MAC2STR(sta->addr));
+ wpa_printf(MSG_DEBUG,
+ "hostapd_sta_bss_transition_check, shell command: %s", shellBuf);
+ system(shellBuf);
+ memset(shellBuf, 0, sizeof(shellBuf));
+ }
+ }
+}
+#endif /* CONFIG_BSS_TRANS_IMPL */
+
+
+void hostapd_periodic_iface(struct hostapd_iface *iface)
+{
+ size_t i;
+
+ ap_list_timer(iface);
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (!hapd->started)
+ continue;
+
+#ifndef CONFIG_NO_RADIUS
+ hostapd_acl_expire(hapd);
+#endif /* CONFIG_NO_RADIUS */
+
+#ifdef CONFIG_BSS_TRANS_IMPL
+ // Add periodic BSS transition check for connected STAs by Liangyu Chu
+ if (hapd->conf->bss_transition)
+ hostapd_sta_bss_transition_check(hapd);
+#endif /* CONFIG_BSS_TRANS_IMPL */
+ }
+}
+
+
+#ifdef CONFIG_OCV
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG, "OCV: Post-CSA SA Query initiation check");
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->post_csa_sa_query)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "OCV: OCVC STA " MACSTR
+ " did not start SA Query after CSA - disconnect",
+ MAC2STR(sta->addr));
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+}
+#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_IEEE80211BE
+struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ u8 link_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h->bss[0];
+ struct hostapd_bss_config *hconf = h_hapd->conf;
+
+ if (!hconf->mld_ap || hconf->mld_id != hapd->conf->mld_id)
+ continue;
+
+ if (h_hapd->mld_link_id == link_id)
+ return h_hapd;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.h
new file mode 100644
index 0000000..8c44e52
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.h
@@ -0,0 +1,805 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common/defs.h"
+#include "common/dpp.h"
+#include "utils/list.h"
+#include "ap_config.h"
+#include "drivers/driver.h"
+#include "ubus.h"
+
+#define OCE_STA_CFON_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_STA_CFON) && \
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
+#define OCE_AP_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_AP) && \
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
+
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hostapd_data;
+struct sta_info;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+#define CTRL_IFACE_COOKIE_LEN 8
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+struct hostapd_iface;
+
+struct hapd_interfaces {
+ int (*reload_config)(struct hostapd_iface *iface, int reconf);
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+ int (*driver_init)(struct hostapd_iface *iface);
+
+ size_t count;
+ int global_ctrl_sock;
+ struct dl_list global_ctrl_dst;
+ char *global_iface_path;
+ char *global_iface_name;
+#ifndef CONFIG_NATIVE_WINDOWS
+ gid_t ctrl_iface_group;
+#endif /* CONFIG_NATIVE_WINDOWS */
+ struct hostapd_iface **iface;
+
+ size_t terminate_on_error;
+#ifndef CONFIG_NO_VLAN
+ struct dynamic_iface *vlan_priv;
+#endif /* CONFIG_NO_VLAN */
+#ifdef CONFIG_ETH_P_OUI
+ struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
+#endif /* CONFIG_ETH_P_OUI */
+ int eloop_initialized;
+
+#ifdef CONFIG_DPP
+ struct dpp_global *dpp;
+#ifdef CONFIG_DPP3
+ struct os_reltime dpp_pb_time;
+ struct os_reltime dpp_pb_announce_time;
+ struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+ struct dpp_bootstrap_info *dpp_pb_bi;
+ u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+ u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+ struct os_reltime dpp_pb_last_resp;
+ bool dpp_pb_result_indicated;
+ char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+ unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ struct ubus_object ubus;
+};
+
+enum hostapd_chan_status {
+ HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+ HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+ HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+ HOSTAPD_CHAN_INVALID_NO_IR = 3, /* channel invalid due to AFC NO IR */
+};
+
+struct hostapd_probereq_cb {
+ int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len, int ssi_signal);
+ void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+ int rate; /* rate in 100 kbps */
+ int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+ unsigned int freq;
+ u32 channel;
+ u32 datarate;
+ int ssi_signal; /* dBm */
+};
+
+enum wps_status {
+ WPS_STATUS_SUCCESS = 1,
+ WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+ WPS_PBC_STATUS_DISABLE,
+ WPS_PBC_STATUS_ACTIVE,
+ WPS_PBC_STATUS_TIMEOUT,
+ WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+ enum wps_status status;
+ enum wps_error_indication failure_reason;
+ enum pbc_status pbc_status;
+ u8 peer_addr[ETH_ALEN];
+};
+
+struct hostapd_neighbor_entry {
+ struct dl_list list;
+ u8 bssid[ETH_ALEN];
+ struct wpa_ssid_value ssid;
+ struct wpabuf *nr;
+ struct wpabuf *lci;
+ struct wpabuf *civic;
+ /* LCI update time */
+ struct os_time lci_date;
+ int stationary;
+ u32 short_ssid;
+ u8 bss_parameters;
+};
+
+struct hostapd_sae_commit_queue {
+ struct dl_list list;
+ int rssi;
+ size_t len;
+ u8 msg[];
+};
+
+/**
+ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
+ */
+struct hostapd_openwrt_stats {
+ struct {
+ u64 neighbor_report_tx;
+ } rrm;
+
+ struct {
+ u64 bss_transition_query_rx;
+ u64 bss_transition_request_tx;
+ u64 bss_transition_response_rx;
+ } wnm;
+};
+
+/**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+struct hostapd_data {
+ struct hostapd_iface *iface;
+ struct hostapd_config *iconf;
+ struct hostapd_bss_config *conf;
+ struct hostapd_ubus_bss ubus;
+ char *config_id;
+ int interface_added; /* virtual interface added for this BSS */
+ unsigned int started:1;
+ unsigned int disabled:1;
+ unsigned int reenable_beacon:1;
+
+ u8 own_addr[ETH_ALEN];
+ u8 mld_addr[ETH_ALEN];
+ u8 mld_link_id;
+ /* Used for mld_link_id assignment - valid on the first MLD BSS only */
+ u8 mld_next_link_id;
+
+ struct hostapd_data *mld_first_bss;
+
+ /* OpenWrt specific statistics */
+ struct hostapd_openwrt_stats openwrt_stats;
+
+ int num_sta; /* number of entries in sta_list */
+ struct sta_info *sta_list; /* STA info list head */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+ struct sta_info *sta_hash[STA_HASH_SIZE];
+
+ /*
+ * Bitfield for indicating which AIDs are allocated. Only AID values
+ * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+ * 1.
+ */
+#define AID_WORDS ((2008 + 31) / 32)
+ u32 sta_aid[AID_WORDS];
+
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+
+ void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+ struct sta_info *sta, int reassoc);
+
+ void *msg_ctx; /* ctx for wpa_msg() calls */
+ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
+
+ struct radius_client_data *radius;
+ u64 acct_session_id;
+ struct radius_das_data *radius_das;
+
+ struct hostapd_cached_radius_acl *acl_cache;
+ struct hostapd_acl_query_data *acl_queries;
+
+ struct wpa_authenticator *wpa_auth;
+ struct eapol_authenticator *eapol_auth;
+ struct eap_config *eap_cfg;
+
+ struct rsn_preauth_interface *preauth_iface;
+ struct os_reltime michael_mic_failure;
+ int michael_mic_failures;
+ int tkip_countermeasures;
+
+ int ctrl_sock;
+ struct dl_list ctrl_dst;
+
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+ struct crypto_rsa_key *imsi_privacy_key;
+ struct radius_server_data *radius_srv;
+ struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+ int parameter_set_count;
+
+ /* Time Advertisement */
+ u8 time_update_counter;
+ struct wpabuf *time_adv;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ struct l2_packet_data *l2;
+
+#ifdef CONFIG_IEEE80211R_AP
+ struct dl_list l2_queue;
+ struct dl_list l2_oui_queue;
+ struct eth_p_oui_ctx *oui_pull;
+ struct eth_p_oui_ctx *oui_resp;
+ struct eth_p_oui_ctx *oui_push;
+ struct eth_p_oui_ctx *oui_sreq;
+ struct eth_p_oui_ctx *oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+
+ struct wps_context *wps;
+
+ int beacon_set_done;
+ struct wpabuf *wps_beacon_ie;
+ struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+ unsigned int ap_pin_failures;
+ unsigned int ap_pin_failures_consecutive;
+ struct upnp_wps_device_sm *wps_upnp;
+ unsigned int ap_pin_lockout_time;
+
+ struct wps_stat wps_stats;
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_MACSEC
+ struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
+
+ struct hostapd_probereq_cb *probereq_cb;
+ size_t num_probereq_cb;
+
+ void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *public_action_cb_ctx;
+ void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *public_action_cb2_ctx;
+
+ int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *vendor_action_cb_ctx;
+
+ void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e);
+ void *wps_reg_success_cb_ctx;
+
+ void (*wps_event_cb)(void *ctx, enum wps_event event,
+ union wps_event_data *data);
+ void *wps_event_cb_ctx;
+
+ void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr);
+ void *sta_authorized_cb_ctx;
+
+ void (*setup_complete_cb)(void *ctx);
+ void *setup_complete_cb_ctx;
+
+ void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
+ size_t psk_len);
+ void *new_psk_cb_ctx;
+
+ /* channel switch parameters */
+ struct hostapd_freq_params cs_freq_params;
+ u8 cs_count;
+ int cs_block_tx;
+ unsigned int cs_c_off_beacon;
+ unsigned int cs_c_off_proberesp;
+ int csa_in_progress;
+ unsigned int cs_c_off_ecsa_beacon;
+ unsigned int cs_c_off_ecsa_proberesp;
+
+#ifdef CONFIG_IEEE80211AX
+ bool cca_in_progress;
+ u8 cca_count;
+ u8 cca_color;
+ unsigned int cca_c_off_beacon;
+ unsigned int cca_c_off_proberesp;
+ struct os_reltime first_color_collision;
+ struct os_reltime last_color_collision;
+ u64 color_collision_bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_P2P
+ struct p2p_data *p2p;
+ struct p2p_group *p2p_group;
+ struct wpabuf *p2p_beacon_ie;
+ struct wpabuf *p2p_probe_resp_ie;
+
+ /* Number of non-P2P association stations */
+ int num_sta_no_p2p;
+
+ /* Periodic NoA (used only when no non-P2P clients in the group) */
+ int noa_enabled;
+ int noa_start;
+ int noa_duration;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_PROXYARP
+ struct l2_packet_data *sock_dhcp;
+ struct l2_packet_data *sock_ndisc;
+ bool x_snoop_initialized;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+ int num_plinks;
+ int max_plinks;
+ void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
+ struct sta_info *sta);
+ struct wpabuf *mesh_pending_auth;
+ struct os_reltime mesh_pending_auth_time;
+ u8 mesh_required_peer[ETH_ALEN];
+#endif /* CONFIG_MESH */
+
+#ifdef CONFIG_SQLITE
+ struct hostapd_eap_user tmp_eap_user;
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+
+#define COMEBACK_KEY_SIZE 8
+#define COMEBACK_PENDING_IDX_SIZE 256
+
+ /** Key used for generating SAE anti-clogging tokens */
+ u8 comeback_key[COMEBACK_KEY_SIZE];
+ struct os_reltime last_comeback_key_update;
+ u16 comeback_idx;
+ u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
+ int dot11RSNASAERetransPeriod; /* msec */
+ struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ unsigned int ext_mgmt_frame_handling:1;
+ unsigned int ext_eapol_frame_io:1;
+
+ struct l2_packet_data *l2_test;
+
+ enum wpa_alg last_gtk_alg;
+ int last_gtk_key_idx;
+ u8 last_gtk[WPA_GTK_MAX_LEN];
+ size_t last_gtk_len;
+
+ enum wpa_alg last_igtk_alg;
+ int last_igtk_key_idx;
+ u8 last_igtk[WPA_IGTK_MAX_LEN];
+ size_t last_igtk_len;
+
+ enum wpa_alg last_bigtk_alg;
+ int last_bigtk_key_idx;
+ u8 last_bigtk[WPA_BIGTK_MAX_LEN];
+ size_t last_bigtk_len;
+
+ bool force_backlog_bytes;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_MBO
+ unsigned int mbo_assoc_disallow;
+#endif /* CONFIG_MBO */
+
+ struct dl_list nr_db;
+
+ u8 beacon_req_token;
+ u8 lci_req_token;
+ u8 range_req_token;
+ unsigned int lci_req_active:1;
+ unsigned int range_req_active:1;
+
+ int dhcp_sock; /* UDP socket used with the DHCP server */
+
+ struct ptksa_cache *ptksa;
+
+#ifdef CONFIG_DPP
+ int dpp_init_done;
+ struct dpp_authentication *dpp_auth;
+ u8 dpp_allowed_roles;
+ int dpp_qr_mutual;
+ int dpp_auth_ok_on_ack;
+ int dpp_in_response_listen;
+ struct gas_query_ap *gas;
+ struct dpp_pkex *dpp_pkex;
+ struct dpp_bootstrap_info *dpp_pkex_bi;
+ char *dpp_pkex_code;
+ size_t dpp_pkex_code_len;
+ char *dpp_pkex_identifier;
+ enum dpp_pkex_ver dpp_pkex_ver;
+ char *dpp_pkex_auth_cmd;
+ char *dpp_configurator_params;
+ struct os_reltime dpp_last_init;
+ struct os_reltime dpp_init_iter_start;
+ unsigned int dpp_init_max_tries;
+ unsigned int dpp_init_retry_time;
+ unsigned int dpp_resp_wait_time;
+ unsigned int dpp_resp_max_tries;
+ unsigned int dpp_resp_retry_time;
+#ifdef CONFIG_DPP2
+ struct wpabuf *dpp_presence_announcement;
+ struct dpp_bootstrap_info *dpp_chirp_bi;
+ int dpp_chirp_freq;
+ int *dpp_chirp_freqs;
+ int dpp_chirp_iter;
+ int dpp_chirp_round;
+ int dpp_chirp_scan_done;
+ int dpp_chirp_listen;
+ struct os_reltime dpp_relay_last_needs_ctrl;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ char *dpp_config_obj_override;
+ char *dpp_discovery_override;
+ char *dpp_groups_override;
+ unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_AIRTIME_POLICY
+ unsigned int num_backlogged_sta;
+ unsigned int airtime_weight;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+ u8 last_1x_eapol_key_replay_counter[8];
+
+#ifdef CONFIG_SQLITE
+ sqlite3 *rad_attr_db;
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+ unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
+#endif /* CONFIG_CTRL_IFACE_UDP */
+};
+
+
+struct hostapd_sta_info {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ struct os_reltime last_seen;
+ int ssi_signal;
+#ifdef CONFIG_TAXONOMY
+ struct wpabuf *probe_ie_taxonomy;
+#endif /* CONFIG_TAXONOMY */
+};
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+ struct hapd_interfaces *interfaces;
+ void *owner;
+ char *config_fname;
+ struct hostapd_config *conf;
+ char phy[16]; /* Name of the PHY (radio) */
+
+ enum hostapd_iface_state {
+ HAPD_IFACE_UNINITIALIZED,
+ HAPD_IFACE_DISABLED,
+ HAPD_IFACE_COUNTRY_UPDATE,
+ HAPD_IFACE_ACS,
+ HAPD_IFACE_HT_SCAN,
+ HAPD_IFACE_DFS,
+ HAPD_IFACE_NO_IR,
+ HAPD_IFACE_ENABLED
+ } state;
+
+#ifdef CONFIG_MESH
+ struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
+
+ size_t num_bss;
+ struct hostapd_data **bss;
+
+ unsigned int wait_channel_update:1;
+ unsigned int cac_started:1;
+#ifdef CONFIG_FST
+ struct fst_iface *fst;
+ const struct wpabuf *fst_ies;
+#endif /* CONFIG_FST */
+
+ /*
+ * When set, indicates that the driver will handle the AP
+ * teardown: delete global keys, station keys, and stations.
+ */
+ unsigned int driver_ap_teardown:1;
+
+ /*
+ * When set, indicates that this interface is part of list of
+ * interfaces that need to be started together (synchronously).
+ */
+ unsigned int need_to_start_in_sync:1;
+
+ /* Ready to start but waiting for other interfaces to become ready. */
+ unsigned int ready_to_start_in_sync:1;
+
+ int num_ap; /* number of entries in ap_list */
+#ifdef CONFIG_5G_BW_SWITCH
+ int max_rssi; /* maximum RSSI level of co-channel APs. Added by Liangyu Chu */
+#endif /* CONFIG_5G_BW_SWITCH */
+ struct ap_info *ap_list; /* AP info list head */
+ struct ap_info *ap_hash[STA_HASH_SIZE];
+
+ u64 drv_flags;
+ u64 drv_flags2;
+
+ /*
+ * A bitmap of supported protocols for probe response offload. See
+ * struct wpa_driver_capa in driver.h
+ */
+ unsigned int probe_resp_offloads;
+
+ /* extended capabilities supported by the driver */
+ const u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+
+ u16 mld_eml_capa, mld_mld_capa;
+
+ unsigned int drv_max_acl_mac_addrs;
+
+ struct hostapd_hw_modes *hw_features;
+ int num_hw_features;
+ struct hostapd_hw_modes *current_mode;
+ /* Rates that are currently used (i.e., filtered copy of
+ * current_mode->channels */
+ int num_rates;
+ struct hostapd_rate_data *current_rates;
+ int *basic_rates;
+ int freq;
+
+ /* Background radar configuration */
+ struct {
+ int channel;
+ int secondary_channel;
+ int freq;
+ int centr_freq_seg0_idx;
+ int centr_freq_seg1_idx;
+ /* Main chain is on temporary channel during
+ * CAC detection on radar offchain.
+ */
+ unsigned int temp_ch:1;
+ /* CAC started on radar offchain */
+ unsigned int cac_started:1;
+ } radar_background;
+
+ u16 hw_flags;
+
+ /* Number of associated Non-ERP stations (i.e., stations using 802.11b
+ * in 802.11g BSS) */
+ int num_sta_non_erp;
+
+ /* Number of associated stations that do not support Short Slot Time */
+ int num_sta_no_short_slot_time;
+
+ /* Number of associated stations that do not support Short Preamble */
+ int num_sta_no_short_preamble;
+
+ int olbc; /* Overlapping Legacy BSS Condition */
+
+ /* Number of HT associated stations that do not support greenfield */
+ int num_sta_ht_no_gf;
+
+ /* Number of associated non-HT stations */
+ int num_sta_no_ht;
+
+ /* Number of HT associated stations 20 MHz */
+ int num_sta_ht_20mhz;
+
+ /* Number of HT40 intolerant stations */
+ int num_sta_ht40_intolerant;
+
+ /* Overlapping BSS information */
+ int olbc_ht;
+
+ u16 ht_op_mode;
+
+ /* surveying helpers */
+
+ /* number of channels surveyed */
+ unsigned int chans_surveyed;
+
+ /* lowest observed noise floor in dBm */
+ s8 lowest_nf;
+
+ /* channel utilization calculation */
+ u64 last_channel_time;
+ u64 last_channel_time_busy;
+ u8 channel_utilization;
+
+ unsigned int chan_util_samples_sum;
+ unsigned int chan_util_num_sample_periods;
+ unsigned int chan_util_average;
+
+ /* eCSA IE will be added only if operating class is specified */
+ u8 cs_oper_class;
+
+ unsigned int dfs_cac_ms;
+ struct os_reltime dfs_cac_start;
+
+ /* Latched with the actual secondary channel information and will be
+ * used while juggling between HT20 and HT40 modes. */
+ int secondary_ch;
+#ifdef CONFIG_5G_BW_SWITCH
+ // Record VHT operation channel bandwidth. Added by Liangyu Chu
+ u8 vht_oper_ch_bw;
+ // Record VHT operation center_freq_seg0_index for 80M bandwidth
+ u8 vht_center_freq_seg0_80m;
+ // Record VHT operation center_freq_seg0_index for 40M bandwidth
+ u8 vht_center_freq_seg0_40m;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_completed_scans;
+#endif /* CONFIG_ACS */
+
+ void (*scan_cb)(struct hostapd_iface *iface);
+ int num_ht40_scan_tries;
+
+ struct dl_list sta_seen; /* struct hostapd_sta_info */
+ unsigned int num_sta_seen;
+
+ u8 dfs_domain;
+#ifdef CONFIG_AIRTIME_POLICY
+ unsigned int airtime_quantum;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+ /* Previous WMM element information */
+ struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
+
+ /* Maximum number of interfaces supported for MBSSID advertisement */
+ unsigned int mbssid_max_interfaces;
+ /* Maximum profile periodicity for enhanced MBSSID advertisement */
+ unsigned int ema_max_periodicity;
+
+ int (*enable_iface_cb)(struct hostapd_iface *iface);
+ int (*disable_iface_cb)(struct hostapd_iface *iface);
+
+ /* Configured freq of interface is NO_IR */
+ bool is_no_ir;
+};
+
+/* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+int hostapd_reload_config(struct hostapd_iface *iface, int reconf);
+void hostapd_reconfig_encryption(struct hostapd_data *hapd);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf,
+ struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+void hostapd_interface_deinit(struct hostapd_iface *iface);
+void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_alloc_iface(void);
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ const char *config_file);
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug);
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc);
+void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_bss_only(struct hostapd_data *bss);
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
+void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
+void hostapd_free_hapd_data(struct hostapd_data *hapd);
+void hostapd_cleanup_iface_partial(struct hostapd_iface *iface);
+int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
+int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_csa_in_progress(struct hostapd_iface *iface);
+void hostapd_chan_switch_config(struct hostapd_data *hapd,
+ struct hostapd_freq_params *freq_params);
+int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+void hostapd_periodic_iface(struct hostapd_iface *iface);
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
+int hostapd_check_max_sta(struct hostapd_data *hapd);
+
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+ int (*cb)(void *ctx, const u8 *sa,
+ const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal),
+ void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
+ int mld_assoc_link_id);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta);
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ielen, int reassoc);
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset, int width, int cf1, int cf2,
+ u16 punct_bitmap, int finished);
+struct survey_results;
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+ struct survey_results *survey_results);
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+ struct acs_selected_channels *acs_res);
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2);
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname);
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+ enum smps_mode smps_mode,
+ enum chan_width chan_width, u8 rx_nss);
+
+#ifdef CONFIG_FST
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+ struct fst_wpa_obj *iface_obj);
+#endif /* CONFIG_FST */
+
+int hostapd_set_acl(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+ u8 link_id);
+
+#endif /* HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.c
new file mode 100644
index 0000000..05e9b9d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.c
@@ -0,0 +1,255 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "hs20.h"
+
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 conf;
+ if (!hapd->conf->hs20)
+ return eid;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_INDICATION_OUI_TYPE;
+ conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
+ if (hapd->conf->hs20_release >= 2)
+ conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+ if (hapd->conf->disable_dgaf)
+ conf |= HS20_DGAF_DISABLED;
+ *eid++ = conf;
+ if (hapd->conf->hs20_release >= 2) {
+ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+ eid += 2;
+ }
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ if (!hapd->conf->osen)
+ return eid;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ if (hapd->conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#ifdef CONFIG_OCV
+ if (hapd->conf->ocv &&
+ (hapd->iface->drv_flags2 &
+ (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url)
+{
+ struct wpabuf *buf;
+ size_t len = 0;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ if (url) {
+ len = 1 + os_strlen(url);
+ if (5 + len > 255) {
+ wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+ "WNM-Notification: '%s'", url);
+ return -1;
+ }
+ }
+
+ buf = wpabuf_alloc(4 + 7 + len);
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Subscription Remediation subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5 + len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+ if (url) {
+ wpabuf_put_u8(buf, len - 1);
+ wpabuf_put_data(buf, url, len - 1);
+ wpabuf_put_u8(buf, osu_method);
+ } else {
+ /* Server URL and Server Method fields not included */
+ wpabuf_put_u8(buf, 0);
+ }
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Deauthentication Imminent Notice subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+ wpabuf_put_buf(buf, payload);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+ const u8 *addr, const char *url)
+{
+ struct wpabuf *buf;
+ int ret;
+ size_t url_len;
+
+ if (!url) {
+ wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
+ return -1;
+ }
+
+ url_len = os_strlen(url);
+ if (5 + url_len > 255) {
+ wpa_printf(MSG_INFO,
+ "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
+ url);
+ return -1;
+ }
+
+ buf = wpabuf_alloc(4 + 7 + url_len);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Terms and Conditions Acceptance subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + 1 + url_len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
+ wpabuf_put_u8(buf, url_len);
+ wpabuf_put_str(buf, url);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+ int enabled)
+{
+ if (enabled) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering required for "
+ MACSTR, MAC2STR(sta->addr));
+ sta->hs20_t_c_filtering = 1;
+ /* TODO: Enable firewall filtering for the STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
+ MAC2STR(sta->addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering not required for "
+ MACSTR, MAC2STR(sta->addr));
+ sta->hs20_t_c_filtering = 0;
+ /* TODO: Disable firewall filtering for the STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
+ }
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.h
new file mode 100644
index 0000000..e99e26e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.h
@@ -0,0 +1,26 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_H
+#define HS20_H
+
+struct hostapd_data;
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload);
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+ const u8 *addr, const char *url);
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+ int enabled);
+
+#endif /* HS20_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.c
new file mode 100644
index 0000000..2152eef
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.c
@@ -0,0 +1,1333 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "acs.h"
+#include "ieee802_11.h"
+#include "beacon.h"
+#include "hw_features.h"
+
+
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+ size_t num_hw_features)
+{
+ size_t i;
+
+ if (hw_features == NULL)
+ return;
+
+ for (i = 0; i < num_hw_features; i++) {
+ os_free(hw_features[i].channels);
+ os_free(hw_features[i].rates);
+ }
+
+ os_free(hw_features);
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+ static char info[256];
+ char *state;
+
+ switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+ case HOSTAPD_CHAN_DFS_UNKNOWN:
+ state = "unknown";
+ break;
+ case HOSTAPD_CHAN_DFS_USABLE:
+ state = "usable";
+ break;
+ case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+ state = "unavailable";
+ break;
+ case HOSTAPD_CHAN_DFS_AVAILABLE:
+ state = "available";
+ break;
+ default:
+ return "";
+ }
+ os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+ info[sizeof(info) - 1] = '\0';
+
+ return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ int i, j;
+ u16 num_modes, flags;
+ struct hostapd_hw_modes *modes;
+ u8 dfs_domain;
+
+ if (hostapd_drv_none(hapd))
+ return -1;
+ modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
+ &dfs_domain);
+ if (modes == NULL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Fetching hardware channel/rate support not "
+ "supported.");
+ return -1;
+ }
+
+ iface->hw_flags = flags;
+ iface->dfs_domain = dfs_domain;
+
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = modes;
+ iface->num_hw_features = num_modes;
+
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *feature = &modes[i];
+ int dfs_enabled = hapd->iconf->ieee80211h &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
+ /* set flag for channels we can use in current regulatory
+ * domain */
+ for (j = 0; j < feature->num_channels; j++) {
+ int dfs = 0;
+
+ /*
+ * Disable all channels that are marked not to allow
+ * to initiate radiation (a.k.a. passive scan and no
+ * IBSS).
+ * Use radar channels only if the driver supports DFS.
+ */
+ if ((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+ dfs = 1;
+ } else if (((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) &&
+ !(iface->drv_flags &
+ WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+ (feature->channels[j].flag &
+ HOSTAPD_CHAN_NO_IR)) {
+ feature->channels[j].flag |=
+ HOSTAPD_CHAN_DISABLED;
+ }
+
+ if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
+ "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
+ feature->mode,
+ feature->channels[j].chan,
+ feature->channels[j].freq,
+ feature->channels[j].max_tx_power,
+ dfs ? dfs_info(&feature->channels[j]) : "");
+ }
+ }
+
+ return 0;
+}
+
+
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
+{
+ int i, num_basic_rates = 0;
+ int basic_rates_a[] = { 60, 120, 240, -1 };
+ int basic_rates_b[] = { 10, 20, -1 };
+ int basic_rates_g[] = { 10, 20, 55, 110, -1 };
+ int *basic_rates;
+
+ if (iface->conf->basic_rates)
+ basic_rates = iface->conf->basic_rates;
+ else switch (mode->mode) {
+ case HOSTAPD_MODE_IEEE80211A:
+ basic_rates = basic_rates_a;
+ break;
+ case HOSTAPD_MODE_IEEE80211B:
+ basic_rates = basic_rates_b;
+ break;
+ case HOSTAPD_MODE_IEEE80211G:
+ basic_rates = basic_rates_g;
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ return 0; /* No basic rates for 11ad */
+ default:
+ return -1;
+ }
+
+ i = 0;
+ while (basic_rates[i] >= 0)
+ i++;
+ if (i)
+ i++; /* -1 termination */
+ os_free(iface->basic_rates);
+ iface->basic_rates = os_malloc(i * sizeof(int));
+ if (iface->basic_rates)
+ os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
+
+ os_free(iface->current_rates);
+ iface->num_rates = 0;
+
+ iface->current_rates =
+ os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
+ if (!iface->current_rates) {
+ wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
+ "table.");
+ return -1;
+ }
+
+ for (i = 0; i < mode->num_rates; i++) {
+ struct hostapd_rate_data *rate;
+
+ if (iface->conf->supported_rates &&
+ !hostapd_rate_found(iface->conf->supported_rates,
+ mode->rates[i]))
+ continue;
+
+ rate = &iface->current_rates[iface->num_rates];
+ rate->rate = mode->rates[i];
+ if (hostapd_rate_found(basic_rates, rate->rate)) {
+ rate->flags |= HOSTAPD_RATE_BASIC;
+ num_basic_rates++;
+ }
+ wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
+ iface->num_rates, rate->rate, rate->flags);
+ iface->num_rates++;
+ }
+
+ if ((iface->num_rates == 0 || num_basic_rates == 0) &&
+ (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
+ wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
+ "rate sets (%d,%d).",
+ iface->num_rates, num_basic_rates);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
+{
+ int pri_freq, sec_freq;
+ struct hostapd_channel_data *p_chan, *s_chan;
+
+ pri_freq = iface->freq;
+ sec_freq = pri_freq + iface->conf->secondary_channel * 20;
+
+ if (!iface->current_mode)
+ return 0;
+
+ p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+
+ s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+
+ return allowed_ht40_channel_pair(iface->current_mode->mode,
+ p_chan, s_chan);
+}
+
+
+static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
+{
+ if (iface->conf->secondary_channel > 0) {
+ iface->conf->channel += 4;
+ iface->freq += 20;
+ iface->conf->secondary_channel = -1;
+ } else {
+ iface->conf->channel -= 4;
+ iface->freq -= 20;
+ iface->conf->secondary_channel = 1;
+ }
+}
+
+
+static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
+ struct wpa_scan_results *scan_res)
+{
+ unsigned int pri_freq, sec_freq;
+ int res;
+ struct hostapd_channel_data *pri_chan, *sec_chan;
+
+ pri_freq = iface->freq;
+ sec_freq = pri_freq + iface->conf->secondary_channel * 20;
+
+ if (!iface->current_mode)
+ return 0;
+ pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq,
+ NULL, iface->hw_features,
+ iface->num_hw_features);
+ sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq,
+ NULL, iface->hw_features,
+ iface->num_hw_features);
+
+ res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
+
+ if (res == 2) {
+ if (iface->conf->no_pri_sec_switch) {
+ wpa_printf(MSG_DEBUG,
+ "Cannot switch PRI/SEC channels due to local constraint");
+ } else {
+ ieee80211n_switch_pri_sec(iface);
+ }
+ }
+
+ return !!res;
+}
+
+
+static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
+ struct wpa_scan_results *scan_res)
+{
+ int pri_chan, sec_chan;
+
+ pri_chan = iface->conf->channel;
+ sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+
+ return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+ sec_chan);
+}
+
+
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
+{
+ struct wpa_scan_results *scan_res;
+ int oper40;
+#ifdef CONFIG_24G_BW_SWITCH
+ int res = 1;
+#else
+ int res = 0;
+#endif /* CONFIG_24G_BW_SWITCH */
+
+ /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
+ * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
+
+ iface->scan_cb = NULL;
+
+ scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+ if (scan_res == NULL) {
+ hostapd_setup_interface_complete(iface, 1);
+ return;
+ }
+
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
+ oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
+ else
+ oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
+ wpa_scan_results_free(scan_res);
+
+ iface->secondary_ch = iface->conf->secondary_channel;
+ if (!oper40) {
+ wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
+ "channel pri=%d sec=%d based on overlapping BSSes",
+ iface->conf->channel,
+ iface->conf->channel +
+ iface->conf->secondary_channel * 4);
+#ifdef CONFIG_24G_BW_SWITCH
+ if (iface->conf->auto_20m_40m)
+#endif /* CONFIG_24G_BW_SWITCH */
+ {
+ iface->conf->secondary_channel = 0;
+ }
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
+ /*
+ * TODO: Could consider scheduling another scan to check
+ * if channel width can be changed if no coex reports
+ * are received from associating stations.
+ */
+ }
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (iface->conf->secondary_channel &&
+ iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+ iface->conf->ieee80211ax) {
+ struct he_capabilities *he_cap;
+
+ he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP];
+ if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) {
+ wpa_printf(MSG_DEBUG,
+ "HE: 40 MHz channel width is not supported in 2.4 GHz; clear secondary channel configuration");
+ iface->conf->secondary_channel = 0;
+ }
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ if (iface->conf->secondary_channel)
+ res = ieee80211n_allowed_ht40_channel_pair(iface);
+ if (!res) {
+ iface->conf->secondary_channel = 0;
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
+ hostapd_set_oper_chwidth(iface->conf, CONF_OPER_CHWIDTH_USE_HT);
+ res = 1;
+ wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+ }
+
+ hostapd_setup_interface_complete(iface, !res);
+}
+
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params)
+{
+ /* Scan only the affected frequency range */
+ int pri_freq, sec_freq;
+ int affected_start, affected_end;
+ int i, pos;
+ struct hostapd_hw_modes *mode;
+
+ if (iface->current_mode == NULL)
+ return;
+
+ pri_freq = iface->freq;
+ if (iface->conf->secondary_channel > 0)
+ sec_freq = pri_freq + 20;
+ else
+ sec_freq = pri_freq - 20;
+ /*
+ * Note: Need to find the PRI channel also in cases where the affected
+ * channel is the SEC channel of a 40 MHz BSS, so need to include the
+ * scanning coverage here to be 40 MHz from the center frequency.
+ */
+ affected_start = (pri_freq + sec_freq) / 2 - 40;
+ affected_end = (pri_freq + sec_freq) / 2 + 40;
+ wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+
+ mode = iface->current_mode;
+ params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+ if (params->freqs == NULL)
+ return;
+ pos = 0;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (chan->freq < affected_start ||
+ chan->freq > affected_end)
+ continue;
+ params->freqs[pos++] = chan->freq;
+ }
+}
+
+
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params)
+{
+ /* Scan only the affected frequency range */
+ int pri_freq;
+ int affected_start, affected_end;
+ int i, pos;
+ struct hostapd_hw_modes *mode;
+
+ if (iface->current_mode == NULL)
+ return;
+
+ pri_freq = iface->freq;
+ if (iface->conf->secondary_channel > 0) {
+ affected_start = pri_freq - 10;
+ affected_end = pri_freq + 30;
+ } else {
+ affected_start = pri_freq - 30;
+ affected_end = pri_freq + 10;
+ }
+ wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+
+ mode = iface->current_mode;
+ params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+ if (params->freqs == NULL)
+ return;
+ pos = 0;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (chan->freq < affected_start ||
+ chan->freq > affected_end)
+ continue;
+ params->freqs[pos++] = chan->freq;
+ }
+}
+
+
+static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+ struct hostapd_iface *iface = eloop_data;
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ os_memset(¶ms, 0, sizeof(params));
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ ieee80211n_scan_channels_2g4(iface, ¶ms);
+ else
+ ieee80211n_scan_channels_5g(iface, ¶ms);
+
+ ret = hostapd_driver_scan(iface->bss[0], ¶ms);
+ iface->num_ht40_scan_tries++;
+ os_free(params.freqs);
+
+ if (ret == -EBUSY &&
+ iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+ ret, strerror(-ret), iface->num_ht40_scan_tries);
+ eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+ return;
+ }
+
+ if (ret == 0) {
+ iface->scan_cb = ieee80211n_check_scan;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "Failed to request a scan in device, bringing up in HT20 mode");
+ iface->conf->secondary_channel = 0;
+ iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
+static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+{
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ /* Check that HT40 is used and PRI / SEC switch is allowed */
+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
+ iface->conf->noscan)
+ return 0;
+
+ hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+ wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+ "40 MHz channel");
+ os_memset(¶ms, 0, sizeof(params));
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ ieee80211n_scan_channels_2g4(iface, ¶ms);
+ else
+ ieee80211n_scan_channels_5g(iface, ¶ms);
+
+ ret = hostapd_driver_scan(iface->bss[0], ¶ms);
+ os_free(params.freqs);
+
+ if (ret == -EBUSY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+ ret, strerror(-ret));
+ iface->num_ht40_scan_tries = 1;
+ eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+ eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+ return 1;
+ }
+
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+ ret, strerror(-ret));
+ return -1;
+ }
+
+ iface->scan_cb = ieee80211n_check_scan;
+ return 1;
+}
+
+
+static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
+{
+ u16 hw = iface->current_mode->ht_capab;
+ u16 conf = iface->conf->ht_capab;
+
+ if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
+ !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [LDPC]");
+ return 0;
+ }
+
+ /*
+ * Driver ACS chosen channel may not be HT40 due to internal driver
+ * restrictions.
+ */
+ if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [HT40*]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
+ !(hw & HT_CAP_INFO_GREEN_FIELD)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [GF]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
+ !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [SHORT-GI-20]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
+ !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [SHORT-GI-40]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [TX-STBC]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
+ (hw & HT_CAP_INFO_RX_STBC_MASK)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [RX-STBC*]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_DELAYED_BA) &&
+ !(hw & HT_CAP_INFO_DELAYED_BA)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [DELAYED-BA]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
+ !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [MAX-AMSDU-7935]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
+ !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [DSSS_CCK-40]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
+ !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [LSIG-TXOP-PROT]");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+#ifdef CONFIG_IEEE80211AC
+static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+{
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ u32 hw = mode->vht_capab;
+ u32 conf = iface->conf->vht_capab;
+
+ wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
+ hw, conf);
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+ iface->conf->bss[0]->vendor_vht &&
+ mode->vht_capab == 0 && iface->hw_features) {
+ int i;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ if (iface->hw_features[i].mode ==
+ HOSTAPD_MODE_IEEE80211A) {
+ mode = &iface->hw_features[i];
+ hw = mode->vht_capab;
+ wpa_printf(MSG_DEBUG,
+ "update hw vht capab based on 5 GHz band: 0x%x",
+ hw);
+ break;
+ }
+ }
+ }
+
+ return ieee80211ac_cap_check(hw, conf);
+}
+#endif /* CONFIG_IEEE80211AC */
+
+
+#ifdef CONFIG_IEEE80211AX
+static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+{
+ return 1;
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+ int ret;
+
+ if (is_6ghz_freq(iface->freq))
+ return 0;
+ if (!iface->conf->ieee80211n)
+ return 0;
+
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
+ (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+ wpa_printf(MSG_DEBUG,
+ "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
+ iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
+ }
+
+ if (!ieee80211n_supported_ht_capab(iface))
+ return -1;
+#ifdef CONFIG_IEEE80211AX
+ if (iface->conf->ieee80211ax &&
+ !ieee80211ax_supported_he_capab(iface))
+ return -1;
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AC
+ if (iface->conf->ieee80211ac &&
+ !ieee80211ac_supported_vht_capab(iface))
+ return -1;
+#endif /* CONFIG_IEEE80211AC */
+ ret = ieee80211n_check_40mhz(iface);
+ if (ret)
+ return ret;
+ if (!ieee80211n_allowed_ht40_channel_pair(iface))
+ return -1;
+
+ return 0;
+}
+
+
+int hostapd_check_edmg_capab(struct hostapd_iface *iface)
+{
+ struct hostapd_hw_modes *mode = iface->hw_features;
+ struct ieee80211_edmg_config edmg;
+
+ if (!iface->conf->enable_edmg)
+ return 0;
+
+ hostapd_encode_edmg_chan(iface->conf->enable_edmg,
+ iface->conf->edmg_channel,
+ iface->conf->channel,
+ &edmg);
+
+ if (mode->edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
+ return 0;
+
+ wpa_printf(MSG_WARNING, "Requested EDMG configuration is not valid");
+ wpa_printf(MSG_INFO, "EDMG capab: channels 0x%x, bw_config %d",
+ mode->edmg.channels, mode->edmg.bw_config);
+ wpa_printf(MSG_INFO,
+ "Requested EDMG configuration: channels 0x%x, bw_config %d",
+ edmg.channels, edmg.bw_config);
+ return -1;
+}
+
+
+int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211AX
+ struct he_capabilities *he_cap;
+ u16 hw;
+
+ if (!iface->current_mode || !is_6ghz_freq(iface->freq))
+ return 0;
+
+ he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP];
+ hw = he_cap->he_6ghz_capa;
+ if (iface->conf->he_6ghz_max_mpdu >
+ ((hw & HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK) >>
+ HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT)) {
+ wpa_printf(MSG_ERROR,
+ "The driver does not support the configured HE 6 GHz Max MPDU length");
+ return -1;
+ }
+
+ if (iface->conf->he_6ghz_max_ampdu_len_exp >
+ ((hw & HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK) >>
+ HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT)) {
+ wpa_printf(MSG_ERROR,
+ "The driver does not support the configured HE 6 GHz Max AMPDU Length Exponent");
+ return -1;
+ }
+
+ if (iface->conf->he_6ghz_rx_ant_pat &&
+ !(hw & HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS)) {
+ wpa_printf(MSG_ERROR,
+ "The driver does not support the configured HE 6 GHz Rx Antenna Pattern");
+ return -1;
+ }
+
+ if (iface->conf->he_6ghz_tx_ant_pat &&
+ !(hw & HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS)) {
+ wpa_printf(MSG_ERROR,
+ "The driver does not support the configured HE 6 GHz Tx Antenna Pattern");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211AX */
+ return 0;
+}
+
+
+/* Returns:
+ * 1 = usable
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
+static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+ int frequency, int primary)
+{
+ struct hostapd_channel_data *chan;
+
+ if (!iface->current_mode)
+ return 0;
+
+ chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL,
+ iface->hw_features, iface->num_hw_features);
+ if (!chan)
+ return 0;
+
+ if ((primary && chan_pri_allowed(chan)) ||
+ (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
+ return 1;
+
+ wpa_printf(MSG_INFO,
+ "Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+ frequency, primary ? "primary" : "secondary",
+ chan->flag,
+ chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+ chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+
+ if (is_6ghz_freq(chan->freq) && (chan->flag & HOSTAPD_CHAN_NO_IR))
+ return -1;
+
+ return 0;
+}
+
+
+static int hostapd_is_usable_edmg(struct hostapd_iface *iface)
+{
+ int i, contiguous = 0;
+ int num_of_enabled = 0;
+ int max_contiguous = 0;
+ int err;
+ struct ieee80211_edmg_config edmg;
+ struct hostapd_channel_data *pri_chan;
+
+ if (!iface->conf->enable_edmg)
+ return 1;
+
+ if (!iface->current_mode)
+ return 0;
+ pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+ iface->freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+ if (!pri_chan)
+ return 0;
+ hostapd_encode_edmg_chan(iface->conf->enable_edmg,
+ iface->conf->edmg_channel,
+ pri_chan->chan,
+ &edmg);
+ if (!(edmg.channels & BIT(pri_chan->chan - 1)))
+ return 0;
+
+ /* 60 GHz channels 1..6 */
+ for (i = 0; i < 6; i++) {
+ int freq = 56160 + 2160 * (i + 1);
+
+ if (edmg.channels & BIT(i)) {
+ contiguous++;
+ num_of_enabled++;
+ } else {
+ contiguous = 0;
+ continue;
+ }
+
+ /* P802.11ay defines that the total number of subfields
+ * set to one does not exceed 4.
+ */
+ if (num_of_enabled > 4)
+ return 0;
+
+ err = hostapd_is_usable_chan(iface, freq, 1);
+ if (err <= 0)
+ return err;
+
+ if (contiguous > max_contiguous)
+ max_contiguous = contiguous;
+ }
+
+ /* Check if the EDMG configuration is valid under the limitations
+ * of P802.11ay.
+ */
+ /* check bw_config against contiguous EDMG channels */
+ switch (edmg.bw_config) {
+ case EDMG_BW_CONFIG_4:
+ if (!max_contiguous)
+ return 0;
+ break;
+ case EDMG_BW_CONFIG_5:
+ if (max_contiguous < 2)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_config *conf = iface->conf;
+ u8 bw, start_chan;
+
+ if (!conf->punct_bitmap)
+ return true;
+
+ if (!conf->ieee80211be) {
+ wpa_printf(MSG_ERROR,
+ "Currently RU puncturing is supported only if ieee80211be is enabled");
+ return false;
+ }
+
+ if (iface->freq >= 2412 && iface->freq <= 2484) {
+ wpa_printf(MSG_ERROR,
+ "RU puncturing not supported in 2.4 GHz");
+ return false;
+ }
+
+ switch (conf->eht_oper_chwidth) {
+ case 0:
+ wpa_printf(MSG_ERROR,
+ "RU puncturing is supported only in 80 MHz and 160 MHz");
+ return false;
+ case 1:
+ bw = 80;
+ start_chan = conf->eht_oper_centr_freq_seg0_idx - 6;
+ break;
+ case 2:
+ bw = 160;
+ start_chan = conf->eht_oper_centr_freq_seg0_idx - 14;
+ break;
+ default:
+ return false;
+ }
+
+ if (!is_punct_bitmap_valid(bw, (conf->channel - start_chan) / 4,
+ conf->punct_bitmap)) {
+ wpa_printf(MSG_ERROR, "Invalid puncturing bitmap");
+ return false;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return true;
+}
+
+
+/* Returns:
+ * 1 = usable
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+ int secondary_freq;
+ struct hostapd_channel_data *pri_chan;
+ int err;
+
+ if (!iface->current_mode)
+ return 0;
+ pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+ iface->freq, NULL,
+ iface->hw_features,
+ iface->num_hw_features);
+ if (!pri_chan) {
+ wpa_printf(MSG_ERROR, "Primary frequency not present");
+ return 0;
+ }
+
+ err = hostapd_is_usable_chan(iface, pri_chan->freq, 1);
+ if (err <= 0) {
+ wpa_printf(MSG_ERROR, "Primary frequency not allowed");
+ return err;
+ }
+ err = hostapd_is_usable_edmg(iface);
+ if (err <= 0)
+ return err;
+
+ if (!hostapd_is_usable_punct_bitmap(iface))
+ return 0;
+
+ if (!iface->conf->secondary_channel)
+ return 1;
+
+ err = hostapd_is_usable_chan(iface, iface->freq +
+ iface->conf->secondary_channel * 20, 0);
+ if (err > 0) {
+ if (iface->conf->secondary_channel == 1 &&
+ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))
+ return 1;
+ if (iface->conf->secondary_channel == -1 &&
+ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))
+ return 1;
+ }
+ if (!iface->conf->ht40_plus_minus_allowed)
+ return err;
+
+ /* Both HT40+ and HT40- are set, pick a valid secondary channel */
+ secondary_freq = iface->freq + 20;
+ err = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
+ iface->conf->secondary_channel = 1;
+ return 1;
+ }
+
+ secondary_freq = iface->freq - 20;
+ err = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
+ iface->conf->secondary_channel = -1;
+ return 1;
+ }
+
+ return err;
+}
+
+
+static bool skip_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
+{
+ int chan;
+
+ if (iface->freq > 0 && !hw_mode_get_channel(mode, iface->freq, &chan))
+ return true;
+
+ if (is_6ghz_op_class(iface->conf->op_class) && iface->freq == 0 &&
+ (mode->mode != HOSTAPD_MODE_IEEE80211A ||
+ mode->num_channels == 0 ||
+ !is_6ghz_freq(mode->channels[0].freq)))
+ return true;
+
+ return false;
+}
+
+
+void hostapd_determine_mode(struct hostapd_iface *iface)
+{
+ int i;
+ enum hostapd_hw_mode target_mode;
+
+ if (iface->current_mode ||
+ iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
+ return;
+
+ if (iface->freq < 4000)
+ target_mode = HOSTAPD_MODE_IEEE80211G;
+ else if (iface->freq > 50000)
+ target_mode = HOSTAPD_MODE_IEEE80211AD;
+ else
+ target_mode = HOSTAPD_MODE_IEEE80211A;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode;
+
+ mode = &iface->hw_features[i];
+ if (mode->mode == target_mode) {
+ if (skip_mode(iface, mode))
+ continue;
+
+ iface->current_mode = mode;
+ iface->conf->hw_mode = mode->mode;
+ break;
+ }
+ }
+
+ if (!iface->current_mode)
+ wpa_printf(MSG_ERROR, "ACS: Cannot decide mode");
+}
+
+
+static enum hostapd_chan_status
+hostapd_check_chans(struct hostapd_iface *iface)
+{
+ if (iface->freq) {
+ int err;
+
+ hostapd_determine_mode(iface);
+
+ err = hostapd_is_usable_chans(iface);
+ if (err <= 0) {
+ if (!err)
+ return HOSTAPD_CHAN_INVALID;
+ return HOSTAPD_CHAN_INVALID_NO_IR;
+ }
+ return HOSTAPD_CHAN_VALID;
+ }
+
+ /*
+ * The user set channel=0 or channel=acs_survey
+ * which is used to trigger ACS.
+ */
+
+ switch (acs_init(iface)) {
+ case HOSTAPD_CHAN_ACS:
+ return HOSTAPD_CHAN_ACS;
+ case HOSTAPD_CHAN_INVALID_NO_IR:
+ return HOSTAPD_CHAN_INVALID_NO_IR;
+ case HOSTAPD_CHAN_VALID:
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ return HOSTAPD_CHAN_INVALID;
+ }
+}
+
+
+static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
+{
+ if (!iface->current_mode) {
+ hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Hardware does not support configured mode");
+ return;
+ }
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Configured channel (%d) or frequency (%d) (secondary_channel=%d) not found from the channel list of the current mode (%d) %s",
+ iface->conf->channel,
+ iface->freq, iface->conf->secondary_channel,
+ iface->current_mode->mode,
+ hostapd_hw_mode_txt(iface->current_mode->mode));
+ hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Hardware does not support configured channel");
+}
+
+
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+ int ret = -1;
+
+ if (err)
+ goto out;
+
+ switch (hostapd_check_chans(iface)) {
+ case HOSTAPD_CHAN_VALID:
+ iface->is_no_ir = false;
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+ ACS_EVENT_COMPLETED "freq=%d channel=%d",
+ iface->freq, iface->conf->channel);
+ break;
+ case HOSTAPD_CHAN_ACS:
+ wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+ hostapd_notify_bad_chans(iface);
+ goto out;
+ case HOSTAPD_CHAN_INVALID_NO_IR:
+ iface->is_no_ir = true;
+ /* fall through */
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ wpa_printf(MSG_ERROR, "ACS picked unusable channels");
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+ hostapd_notify_bad_chans(iface);
+ goto out;
+ }
+
+ ret = hostapd_check_ht_capab(iface);
+ if (ret < 0)
+ goto out;
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
+ return 0;
+ }
+
+ ret = 0;
+out:
+ return hostapd_setup_interface_complete(iface, ret);
+}
+
+
+/**
+ * hostapd_select_hw_mode - Select the hardware mode
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, < 0 on failure
+ *
+ * Sets up the hardware mode, channel, rates, and passive scanning
+ * based on the configuration.
+ */
+int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+ int i;
+
+ if (iface->num_hw_features < 1)
+ return -1;
+
+ if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ iface->conf->ieee80211n || iface->conf->ieee80211ac ||
+ iface->conf->ieee80211ax || iface->conf->ieee80211be) &&
+ iface->conf->channel == 14) {
+ wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE/EHT on channel 14");
+ iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+ iface->conf->ieee80211n = 0;
+ iface->conf->ieee80211ac = 0;
+ iface->conf->ieee80211ax = 0;
+ iface->conf->ieee80211be = 0;
+ }
+
+ iface->current_mode = NULL;
+ for (i = 0; i < iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode = &iface->hw_features[i];
+
+ if (mode->mode == iface->conf->hw_mode) {
+ if (skip_mode(iface, mode))
+ continue;
+
+ iface->current_mode = mode;
+ break;
+ }
+ }
+
+ if (iface->current_mode == NULL) {
+ if ((iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) {
+ wpa_printf(MSG_DEBUG,
+ "Using offloaded hw_mode=any ACS");
+ } else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
+ iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) {
+ wpa_printf(MSG_DEBUG,
+ "Using internal ACS for hw_mode=any");
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Hardware does not support configured mode");
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
+ (int) iface->conf->hw_mode);
+ return -2;
+ }
+ }
+
+ switch (hostapd_check_chans(iface)) {
+ case HOSTAPD_CHAN_VALID:
+ iface->is_no_ir = false;
+ return 0;
+ case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
+ return 1;
+ case HOSTAPD_CHAN_INVALID_NO_IR:
+ iface->is_no_ir = true;
+ /* fall through */
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ hostapd_notify_bad_chans(iface);
+ return -3;
+ }
+}
+
+
+const char * hostapd_hw_mode_txt(int mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211A:
+ return "IEEE 802.11a";
+ case HOSTAPD_MODE_IEEE80211B:
+ return "IEEE 802.11b";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "IEEE 802.11g";
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "IEEE 802.11ad";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+ return hw_get_freq(hapd->iface->current_mode, chan);
+}
+
+
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+{
+ int i, channel;
+ struct hostapd_hw_modes *mode;
+
+ if (hapd->iface->current_mode) {
+ channel = hw_get_chan(hapd->iface->current_mode->mode, freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ if (channel)
+ return channel;
+ }
+
+ /* Check other available modes since the channel list for the current
+ * mode did not include the specified frequency. */
+ if (!hapd->iface->hw_features)
+ return 0;
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+ channel = hw_get_chan(mode->mode, freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ if (channel)
+ return channel;
+ }
+ return 0;
+}
+
+
+int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
+{
+ int i;
+
+ if (iface->current_mode)
+ return mode != iface->current_mode;
+ if (mode->mode != HOSTAPD_MODE_IEEE80211B)
+ return 0;
+ for (i = 0; i < iface->num_hw_features; i++) {
+ if (iface->hw_features[i].mode == HOSTAPD_MODE_IEEE80211G)
+ return 1;
+ }
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.h
new file mode 100644
index 0000000..092941f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.h
@@ -0,0 +1,101 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_H
+#define HW_FEATURES_H
+
+#ifdef NEED_AP_MLME
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+ size_t num_hw_features);
+int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface, int err);
+int hostapd_select_hw_mode(struct hostapd_iface *iface);
+const char * hostapd_hw_mode_txt(int mode);
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_check_edmg_capab(struct hostapd_iface *iface);
+int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface);
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode);
+void hostapd_stop_setup_timers(struct hostapd_iface *iface);
+int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode);
+void hostapd_determine_mode(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline void
+hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+ size_t num_hw_features)
+{
+}
+
+static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+ return -1;
+}
+
+static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+ return -1;
+}
+
+static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+ return -100;
+}
+
+static inline const char * hostapd_hw_mode_txt(int mode)
+{
+ return "UNKNOWN";
+}
+
+static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+ return -1;
+}
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+static inline int hostapd_check_edmg_capab(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
+{
+ return 0;
+}
+
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
+static inline int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
+{
+ return 0;
+}
+
+static inline int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+static inline void hostapd_determine_mode(struct hostapd_iface *iface)
+{
+}
+
+#endif /* NEED_AP_MLME */
+
+#endif /* HW_FEATURES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.c
new file mode 100644
index 0000000..634e795
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.c
@@ -0,0 +1,7950 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "common/dpp.h"
+#include "common/ocv.h"
+#include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/ptksa_cache.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "beacon.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wmm.h"
+#include "ap_list.h"
+#include "accounting.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "wnm_ap.h"
+#include "hw_features.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+#include "mbo_ap.h"
+#include "rrm.h"
+#include "taxonomy.h"
+#include "fils_hlp.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
+#include "comeback_token.h"
+#include "pasn/pasn_common.h"
+
+
+#ifdef CONFIG_FILS
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 *resp,
+ struct rsn_pmksa_cache_entry *pmksa,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len,
+ int *is_pub);
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_FILS
+
+static void pasn_fils_auth_resp(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 status,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len);
+
+#endif /* CONFIG_FILS */
+#endif /* CONFIG_PASN */
+
+static void handle_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi, int from_queue);
+static int add_associated_sta(struct hostapd_data *hapd,
+ struct sta_info *sta, int reassoc);
+
+
+u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 multi_ap_val = 0;
+
+ if (!hapd->conf->multi_ap)
+ return eid;
+ if (hapd->conf->multi_ap & BACKHAUL_BSS)
+ multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
+
+ return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+}
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ int i, num, count;
+ int h2e_required;
+
+ if (hapd->iface->current_rates == NULL)
+ return eid;
+
+ *pos++ = WLAN_EID_SUPP_RATES;
+ num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+ num++;
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+ num++;
+#endif /* CONFIG_IEEE80211AX */
+ h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
+ if (h2e_required)
+ num++;
+ if (num > 8) {
+ /* rest of the rates are encoded in Extended supported
+ * rates element */
+ num = 8;
+ }
+
+ *pos++ = num;
+ for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
+ i++) {
+ count++;
+ *pos = hapd->iface->current_rates[i].rate / 5;
+ if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+ *pos |= 0x80;
+ pos++;
+ }
+
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+ }
+
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ if (h2e_required && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+ }
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ int i, num, count;
+ int h2e_required;
+
+ hapd->conf->xrates_supported = false;
+ if (hapd->iface->current_rates == NULL)
+ return eid;
+
+ num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+ num++;
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+ num++;
+#endif /* CONFIG_IEEE80211AX */
+ h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
+ if (h2e_required)
+ num++;
+ if (num <= 8)
+ return eid;
+ num -= 8;
+
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = num;
+ for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
+ i++) {
+ count++;
+ if (count <= 8)
+ continue; /* already in SuppRates IE */
+ *pos = hapd->iface->current_rates[i].rate / 5;
+ if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+ *pos |= 0x80;
+ pos++;
+ }
+
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+ }
+
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ if (h2e_required) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+ }
+
+ hapd->conf->xrates_supported = true;
+ return pos;
+}
+
+
+u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+ if (hapd->conf->radio_measurements[i])
+ break;
+ }
+
+ if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
+ return eid;
+
+ *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+ *eid++ = RRM_CAPABILITIES_IE_LEN;
+ os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
+
+ return eid + RRM_CAPABILITIES_IE_LEN;
+}
+
+
+u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+{
+ int capab = WLAN_CAPABILITY_ESS;
+ int privacy = 0;
+ int dfs;
+ int i;
+
+ /* Check if any of configured channels require DFS */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0) {
+ wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+ dfs);
+ dfs = 0;
+ }
+
+ if (hapd->iface->num_sta_no_short_preamble == 0 &&
+ hapd->iconf->preamble == SHORT_PREAMBLE)
+ capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+#ifdef CONFIG_WEP
+ privacy = hapd->conf->ssid.wep.keys_set;
+
+ if (hapd->conf->ieee802_1x &&
+ (hapd->conf->default_wep_key_len ||
+ hapd->conf->individual_wep_key_len))
+ privacy = 1;
+#endif /* CONFIG_WEP */
+
+ if (hapd->conf->wpa)
+ privacy = 1;
+
+#ifdef CONFIG_HS20
+ if (hapd->conf->osen)
+ privacy = 1;
+#endif /* CONFIG_HS20 */
+
+ if (privacy)
+ capab |= WLAN_CAPABILITY_PRIVACY;
+
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+ hapd->iface->num_sta_no_short_slot_time == 0)
+ capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+ /*
+ * Currently, Spectrum Management capability bit is set when directly
+ * requested in configuration by spectrum_mgmt_required or when AP is
+ * running on DFS channel.
+ * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
+ */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ (hapd->iconf->spectrum_mgmt_required || dfs))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+ for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+ if (hapd->conf->radio_measurements[i]) {
+ capab |= IEEE80211_CAP_RRM;
+ break;
+ }
+ }
+
+ return capab;
+}
+
+
+#ifdef CONFIG_WEP
+#ifndef CONFIG_NO_RC4
+static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 auth_transaction, const u8 *challenge,
+ int iswep)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication (shared key, transaction %d)",
+ auth_transaction);
+
+ if (auth_transaction == 1) {
+ if (!sta->challenge) {
+ /* Generate a pseudo-random challenge */
+ u8 key[8];
+
+ sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
+ if (sta->challenge == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (os_get_random(key, sizeof(key)) < 0) {
+ os_free(sta->challenge);
+ sta->challenge = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ rc4_skip(key, sizeof(key), 0,
+ sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
+ }
+ return 0;
+ }
+
+ if (auth_transaction != 3)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /* Transaction 3 */
+ if (!iswep || !sta->challenge || !challenge ||
+ os_memcmp_const(sta->challenge, challenge,
+ WLAN_AUTH_CHALLENGE_LEN)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "shared key authentication - invalid "
+ "challenge-response");
+ return WLAN_STATUS_CHALLENGE_FAIL;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (shared key)");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ os_free(sta->challenge);
+ sta->challenge = NULL;
+
+ return 0;
+}
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_WEP */
+
+
+static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *dst, const u8 *bssid,
+ u16 auth_alg, u16 auth_transaction, u16 resp,
+ const u8 *ies, size_t ies_len, const char *dbg)
+{
+ struct ieee80211_mgmt *reply;
+ u8 *buf;
+ size_t rlen;
+ int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ const u8 *sa = hapd->own_addr;
+ struct wpabuf *ml_resp = NULL;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once a non-AP MLD is added to the driver, the addressing should use
+ * the MLD MAC address. Thus, use the MLD address instead of translating
+ * the addresses.
+ */
+ if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ sa = hapd->mld_addr;
+
+ ml_resp = hostapd_ml_auth_resp(hapd);
+ if (!ml_resp)
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+ if (ml_resp)
+ rlen += wpabuf_len(ml_resp);
+ buf = os_zalloc(rlen);
+ if (!buf) {
+ wpabuf_free(ml_resp);
+ return -1;
+ }
+
+ reply = (struct ieee80211_mgmt *) buf;
+ reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_AUTH);
+ os_memcpy(reply->da, dst, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+ os_memcpy(reply->bssid, bssid, ETH_ALEN);
+
+ reply->u.auth.auth_alg = host_to_le16(auth_alg);
+ reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+ reply->u.auth.status_code = host_to_le16(resp);
+
+ if (ies && ies_len)
+ os_memcpy(reply->u.auth.variable, ies, ies_len);
+
+#ifdef CONFIG_IEEE80211BE
+ if (ml_resp)
+ os_memcpy(reply->u.auth.variable + ies_len,
+ wpabuf_head(ml_resp), wpabuf_len(ml_resp));
+
+ wpabuf_free(ml_resp);
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
+ " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
+ MAC2STR(dst), auth_alg, auth_transaction,
+ resp, (unsigned long) ies_len, dbg);
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_SAE
+ if (hapd->conf->sae_confirm_immediate == 2 &&
+ auth_alg == WLAN_AUTH_SAE) {
+ if (auth_transaction == 1 && sta &&
+ (resp == WLAN_STATUS_SUCCESS ||
+ resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ resp == WLAN_STATUS_SAE_PK)) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: Postpone SAE Commit transmission until Confirm is ready");
+ os_free(sta->sae_postponed_commit);
+ sta->sae_postponed_commit = buf;
+ sta->sae_postponed_commit_len = rlen;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (auth_transaction == 2 && sta && sta->sae_postponed_commit) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm");
+ if (hostapd_drv_send_mlme(hapd,
+ sta->sae_postponed_commit,
+ sta->sae_postponed_commit_len,
+ 0, NULL, 0, 0) < 0)
+ wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+ os_free(sta->sae_postponed_commit);
+ sta->sae_postponed_commit = NULL;
+ sta->sae_postponed_commit_len = 0;
+ }
+ }
+#endif /* CONFIG_SAE */
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0)
+ wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+ else
+ reply_res = WLAN_STATUS_SUCCESS;
+
+ os_free(buf);
+
+ return reply_res;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ int reply_res;
+
+ reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT,
+ auth_transaction, status, ies, ies_len,
+ "auth-ft-finish");
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL)
+ return;
+
+ if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
+ status != WLAN_STATUS_SUCCESS)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ return;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ return;
+
+ hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ sta->flags |= WLAN_STA_AUTH;
+ mlme_authenticate_indication(hapd, sta);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifdef CONFIG_SAE
+
+static void sae_set_state(struct sta_info *sta, enum sae_state state,
+ const char *reason)
+{
+ wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
+ sae_state_txt(sta->sae->state), sae_state_txt(state),
+ MAC2STR(sta->addr), reason);
+ sta->sae->state = state;
+}
+
+
+static const char * sae_get_password(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const char *rx_id,
+ struct sae_password_entry **pw_entry,
+ struct sae_pt **s_pt,
+ const struct sae_pk **s_pk)
+{
+ const char *password = NULL;
+ struct sae_password_entry *pw;
+ struct sae_pt *pt = NULL;
+ const struct sae_pk *pk = NULL;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+ continue;
+ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+ continue;
+ if (rx_id && pw->identifier &&
+ os_strcmp(rx_id, pw->identifier) != 0)
+ continue;
+ password = pw->password;
+ pt = pw->pt;
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ pk = pw->pk;
+ break;
+ }
+ if (!password) {
+ password = hapd->conf->ssid.wpa_passphrase;
+ pt = hapd->conf->ssid.pt;
+ }
+
+ if (!password) {
+ for (psk = sta->psk; psk; psk = psk->next) {
+ if (psk->is_passphrase) {
+ password = psk->passphrase;
+ break;
+ }
+ }
+ }
+
+ if (pw_entry)
+ *pw_entry = pw;
+ if (s_pt)
+ *s_pt = pt;
+ if (s_pk)
+ *s_pk = pk;
+
+ return password;
+}
+
+
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+ struct sta_info *sta, int update,
+ int status_code)
+{
+ struct wpabuf *buf;
+ const char *password = NULL;
+ struct sae_password_entry *pw;
+ const char *rx_id = NULL;
+ int use_pt = 0;
+ struct sae_pt *pt = NULL;
+ const struct sae_pk *pk = NULL;
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta)
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (sta->sae->tmp) {
+ rx_id = sta->sae->tmp->pw_id;
+ use_pt = sta->sae->h2e;
+#ifdef CONFIG_SAE_PK
+ os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
+ os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
+#endif /* CONFIG_SAE_PK */
+ }
+
+ if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ use_pt = 1;
+ else if (status_code == WLAN_STATUS_SUCCESS)
+ use_pt = 0;
+ else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK)
+ use_pt = 1;
+
+ password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
+ if (!password || (use_pt && !pt)) {
+ wpa_printf(MSG_DEBUG, "SAE: No password available");
+ return NULL;
+ }
+
+ if (update && use_pt &&
+ sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
+ NULL, pk) < 0)
+ return NULL;
+
+ if (update && !use_pt &&
+ sae_prepare_commit(own_addr, sta->addr,
+ (u8 *) password, os_strlen(password),
+ sta->sae) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+ return NULL;
+ }
+
+ if (pw && pw->vlan_id) {
+ if (!sta->sae->tmp) {
+ wpa_printf(MSG_INFO,
+ "SAE: No temporary data allocated - cannot store VLAN ID");
+ return NULL;
+ }
+ sta->sae->tmp->vlan_id = pw->vlan_id;
+ }
+
+ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+ (rx_id ? 3 + os_strlen(rx_id) : 0));
+ if (buf &&
+ sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+ sta->sae->tmp->anti_clogging_token : NULL,
+ rx_id) < 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
+ if (buf == NULL)
+ return NULL;
+
+#ifdef CONFIG_SAE_PK
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sta->sae->tmp)
+ sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
+
+ if (sae_write_confirm(sta->sae, buf) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *bssid, int update, int status_code)
+{
+ struct wpabuf *data;
+ int reply_res;
+ u16 status;
+
+ data = auth_build_sae_commit(hapd, sta, update, status_code);
+ if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+ if (data == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (sta->sae->tmp && sta->sae->pk)
+ status = WLAN_STATUS_SAE_PK;
+ else if (sta->sae->tmp && sta->sae->h2e)
+ status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+ else
+ status = WLAN_STATUS_SUCCESS;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->sae_commit_status >= 0 &&
+ hapd->conf->sae_commit_status != status) {
+ wpa_printf(MSG_INFO,
+ "TESTING: Override SAE commit status code %u --> %d",
+ status, hapd->conf->sae_commit_status);
+ status = hapd->conf->sae_commit_status;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+ WLAN_AUTH_SAE, 1,
+ status, wpabuf_head(data),
+ wpabuf_len(data), "sae-send-commit");
+
+ wpabuf_free(data);
+
+ return reply_res;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *bssid)
+{
+ struct wpabuf *data;
+ int reply_res;
+
+ data = auth_build_sae_confirm(hapd, sta);
+ if (data == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+ WLAN_AUTH_SAE, 2,
+ WLAN_STATUS_SUCCESS, wpabuf_head(data),
+ wpabuf_len(data), "sae-send-confirm");
+
+ wpabuf_free(data);
+
+ return reply_res;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int use_anti_clogging(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ unsigned int open = 0;
+
+ if (hapd->conf->anti_clogging_threshold == 0)
+ return 1;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+#ifdef CONFIG_SAE
+ if (sta->sae &&
+ (sta->sae->state == SAE_COMMITTED ||
+ sta->sae->state == SAE_CONFIRMED))
+ open++;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_PASN
+ if (sta->pasn && sta->pasn->ecdh)
+ open++;
+#endif /* CONFIG_PASN */
+ if (open >= hapd->conf->anti_clogging_threshold)
+ return 1;
+ }
+
+#ifdef CONFIG_SAE
+ /* In addition to already existing open SAE sessions, check whether
+ * there are enough pending commit messages in the processing queue to
+ * potentially result in too many open sessions. */
+ if (open + dl_list_len(&hapd->sae_commit_queue) >=
+ hapd->conf->anti_clogging_threshold)
+ return 1;
+#endif /* CONFIG_SAE */
+
+ return 0;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
+
+
+#ifdef CONFIG_SAE
+
+static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->sae->sync > hapd->conf->sae_sync) {
+ sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
+ sta->sae->sync = 0;
+ return -1;
+ }
+ return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = eloop_data;
+ int ret;
+
+ if (sae_check_big_sync(hapd, sta))
+ return;
+ sta->sae->sync++;
+ wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
+ " (sync=%d state=%s)",
+ MAC2STR(sta->addr), sta->sae->sync,
+ sae_state_txt(sta->sae->state));
+
+ switch (sta->sae->state) {
+ case SAE_COMMITTED:
+ ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+ eloop_register_timeout(0,
+ hapd->dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+ break;
+ case SAE_CONFIRMED:
+ ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+ eloop_register_timeout(0,
+ hapd->dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret != WLAN_STATUS_SUCCESS)
+ wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ return;
+
+ eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+ eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 status)
+{
+ struct external_auth params;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.status = status;
+ params.bssid = sta->addr;
+ if (status == WLAN_STATUS_SUCCESS && sta->sae &&
+ !hapd->conf->disable_pmksa_caching)
+ params.pmkid = sta->sae->pmkid;
+
+ hostapd_drv_send_external_auth_status(hapd, ¶ms);
+}
+
+
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifndef CONFIG_NO_VLAN
+ struct vlan_description vlan_desc;
+
+ if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
+ " to VLAN ID %d",
+ MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = 1;
+ vlan_desc.untagged = sta->sae->tmp->vlan_id;
+ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ wpa_printf(MSG_INFO,
+ "Invalid VLAN ID %d in sae_password",
+ sta->sae->tmp->vlan_id);
+ return;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
+ ap_sta_bind_vlan(hapd, sta) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to assign VLAN ID %d from sae_password to "
+ MACSTR, sta->sae->tmp->vlan_id,
+ MAC2STR(sta->addr));
+ return;
+ }
+ }
+#endif /* CONFIG_NO_VLAN */
+
+ sta->flags |= WLAN_STA_AUTH;
+ sta->auth_alg = WLAN_AUTH_SAE;
+ mlme_authenticate_indication(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
+ crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
+ sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
+ sta->sae->peer_commit_scalar = NULL;
+ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+ sta->sae->pmk, sta->sae->pmk_len,
+ sta->sae->pmkid, sta->sae->akmp);
+ sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
+}
+
+
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *bssid, u16 auth_transaction, u16 status_code,
+ int allow_reuse, int *sta_removed)
+{
+ int ret;
+
+ *sta_removed = 0;
+
+ if (auth_transaction != 1 && auth_transaction != 2)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
+ MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
+ auth_transaction);
+ switch (sta->sae->state) {
+ case SAE_NOTHING:
+ if (auth_transaction == 1) {
+ if (sta->sae->tmp) {
+ sta->sae->h2e =
+ (status_code ==
+ WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK);
+ sta->sae->pk =
+ status_code == WLAN_STATUS_SAE_PK;
+ }
+ ret = auth_sae_send_commit(hapd, sta, bssid,
+ !allow_reuse, status_code);
+ if (ret)
+ return ret;
+ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /*
+ * In mesh case, both Commit and Confirm are sent
+ * immediately. In infrastructure BSS, by default, only
+ * a single Authentication frame (Commit) is expected
+ * from the AP here and the second one (Confirm) will
+ * be sent once the STA has sent its second
+ * Authentication frame (Confirm). This behavior can be
+ * overridden with explicit configuration so that the
+ * infrastructure BSS case sends both frames together.
+ */
+ if ((hapd->conf->mesh & MESH_ENABLED) ||
+ hapd->conf->sae_confirm_immediate) {
+ /*
+ * Send both Commit and Confirm immediately
+ * based on SAE finite state machine
+ * Nothing -> Confirm transition.
+ */
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+ sae_set_state(sta, SAE_CONFIRMED,
+ "Sent Confirm (mesh)");
+ } else {
+ /*
+ * For infrastructure BSS, send only the Commit
+ * message now to get alternating sequence of
+ * Authentication frames between the AP and STA.
+ * Confirm will be sent in
+ * Committed -> Confirmed/Accepted transition
+ * when receiving Confirm from STA.
+ */
+ }
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "SAE confirm before commit");
+ }
+ break;
+ case SAE_COMMITTED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else if (hapd->conf->mesh & MESH_ENABLED) {
+ /*
+ * In mesh case, follow SAE finite state machine and
+ * send Commit now, if sync count allows.
+ */
+ if (sae_check_big_sync(hapd, sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_commit(hapd, sta, bssid, 0,
+ status_code);
+ if (ret)
+ return ret;
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ /*
+ * For instructure BSS, send the postponed Confirm from
+ * Nothing -> Confirmed transition that was reduced to
+ * Nothing -> Committed above.
+ */
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+
+ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
+
+ /*
+ * Since this was triggered on Confirm RX, run another
+ * step to get to Accepted without waiting for
+ * additional events.
+ */
+ return sae_sm_step(hapd, sta, bssid, auth_transaction,
+ WLAN_STATUS_SUCCESS, 0, sta_removed);
+ }
+ break;
+ case SAE_CONFIRMED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+ if (sae_check_big_sync(hapd, sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1,
+ status_code);
+ if (ret)
+ return ret;
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ sta->sae->send_confirm = 0xffff;
+ sae_accept_sta(hapd, sta);
+ }
+ break;
+ case SAE_ACCEPTED:
+ if (auth_transaction == 1 &&
+ (hapd->conf->mesh & MESH_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+ ") doing reauthentication",
+ MAC2STR(sta->addr));
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+ ap_free_sta(hapd, sta);
+ *sta_removed = 1;
+ } else if (auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1,
+ status_code);
+ if (ret)
+ return ret;
+ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ if (sae_check_big_sync(hapd, sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ sae_clear_temp_data(sta->sae);
+ if (ret)
+ return ret;
+ }
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+ sta->sae->state);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sae_data *sae = sta->sae;
+ int i, *groups = hapd->conf->sae_groups;
+ int default_groups[] = { 19, 0 };
+
+ if (sae->state != SAE_COMMITTED)
+ return;
+
+ wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
+
+ if (!groups)
+ groups = default_groups;
+ for (i = 0; groups[i] > 0; i++) {
+ if (sae->group == groups[i])
+ break;
+ }
+
+ if (groups[i] <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Previously selected group not found from the current configuration");
+ return;
+ }
+
+ for (;;) {
+ i++;
+ if (groups[i] <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No alternative group enabled");
+ return;
+ }
+
+ if (sae_set_group(sae, groups[i]) < 0)
+ continue;
+
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
+}
+
+
+static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
+{
+ enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
+ int id_in_use;
+ bool sae_pk = false;
+
+ id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
+ if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
+ sae_pwe = SAE_PWE_BOTH;
+#ifdef CONFIG_SAE_PK
+ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
+ if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
+ sae_pwe = SAE_PWE_BOTH;
+#endif /* CONFIG_SAE_PK */
+ if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+ (hapd->conf->wpa_key_mgmt &
+ (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
+ sae_pwe = SAE_PWE_BOTH;
+
+ return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
+ sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
+ status_code == WLAN_STATUS_SUCCESS) ||
+ (sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
+ (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ (sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
+ (sae_pwe == SAE_PWE_BOTH &&
+ (status_code == WLAN_STATUS_SUCCESS ||
+ status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ (sae_pk && status_code == WLAN_STATUS_SAE_PK)));
+}
+
+
+static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
+{
+ int *groups = hapd->conf->sae_groups;
+ int default_groups[] = { 19, 0 };
+ int i;
+
+ if (!groups)
+ groups = default_groups;
+
+ for (i = 0; groups[i] > 0; i++) {
+ if (groups[i] == group)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int check_sae_rejected_groups(struct hostapd_data *hapd,
+ struct sae_data *sae)
+{
+ const struct wpabuf *groups;
+ size_t i, count;
+ const u8 *pos;
+
+ if (!sae->tmp)
+ return 0;
+ groups = sae->tmp->peer_rejected_groups;
+ if (!groups)
+ return 0;
+
+ pos = wpabuf_head(groups);
+ count = wpabuf_len(groups) / 2;
+ for (i = 0; i < count; i++) {
+ int enabled;
+ u16 group;
+
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+ enabled = sae_is_group_enabled(hapd, group);
+ wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
+ group, enabled ? "enabled" : "disabled");
+ if (enabled)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ u16 auth_transaction, u16 status_code)
+{
+ int resp = WLAN_STATUS_SUCCESS;
+ struct wpabuf *data = NULL;
+ int *groups = hapd->conf->sae_groups;
+ int default_groups[] = { 19, 0 };
+ const u8 *pos, *end;
+ int sta_removed = 0;
+ bool success_status;
+
+ if (!groups)
+ groups = default_groups;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + len;
+ resp = status_code;
+ send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp, pos, end - pos,
+ "auth-sae-reflection-attack");
+ goto remove_sta;
+ }
+
+ if (hapd->conf->sae_commit_override && auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+ send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp,
+ wpabuf_head(hapd->conf->sae_commit_override),
+ wpabuf_len(hapd->conf->sae_commit_override),
+ "sae-commit-override");
+ goto remove_sta;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!sta->sae) {
+ if (auth_transaction != 1 ||
+ !sae_status_success(hapd, status_code)) {
+ wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
+ status_code);
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ sta->sae = os_zalloc(sizeof(*sta->sae));
+ if (!sta->sae) {
+ resp = -1;
+ goto remove_sta;
+ }
+ sae_set_state(sta, SAE_NOTHING, "Init");
+ sta->sae->sync = 0;
+ }
+
+ if (sta->mesh_sae_pmksa_caching) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+ sta->mesh_sae_pmksa_caching = 0;
+ }
+
+ if (auth_transaction == 1) {
+ const u8 *token = NULL;
+ size_t token_len = 0;
+ int allow_reuse = 0;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "start SAE authentication (RX commit, status=%u (%s))",
+ status_code, status2str(status_code));
+
+ if ((hapd->conf->mesh & MESH_ENABLED) &&
+ status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+ sta->sae->tmp) {
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + len;
+ if (pos + sizeof(le16) > end) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Too short anti-clogging token request");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ resp = sae_group_allowed(sta->sae, groups,
+ WPA_GET_LE16(pos));
+ if (resp != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Invalid group in anti-clogging token request");
+ goto reply;
+ }
+ pos += sizeof(le16);
+
+ wpabuf_free(sta->sae->tmp->anti_clogging_token);
+ sta->sae->tmp->anti_clogging_token =
+ wpabuf_alloc_copy(pos, end - pos);
+ if (sta->sae->tmp->anti_clogging_token == NULL) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Failed to alloc for anti-clogging token");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto remove_sta;
+ }
+
+ /*
+ * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
+ * is 76, a new Commit Message shall be constructed
+ * with the Anti-Clogging Token from the received
+ * Authentication frame, and the commit-scalar and
+ * COMMIT-ELEMENT previously sent.
+ */
+ resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0,
+ status_code);
+ if (resp != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Failed to send commit message");
+ goto remove_sta;
+ }
+ sae_set_state(sta, SAE_COMMITTED,
+ "Sent Commit (anti-clogging token case in mesh)");
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ return;
+ }
+
+ if ((hapd->conf->mesh & MESH_ENABLED) &&
+ status_code ==
+ WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+ sta->sae->tmp) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Peer did not accept our SAE group");
+ sae_pick_next_group(hapd, sta);
+ goto remove_sta;
+ }
+
+ if (!sae_status_success(hapd, status_code))
+ goto remove_sta;
+
+ if (!(hapd->conf->mesh & MESH_ENABLED) &&
+ sta->sae->state == SAE_COMMITTED) {
+ /* This is needed in the infrastructure BSS case to
+ * address a sequence where a STA entry may remain in
+ * hostapd across two attempts to do SAE authentication
+ * by the same STA. The second attempt may end up trying
+ * to use a different group and that would not be
+ * allowed if we remain in Committed state with the
+ * previously set parameters. */
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + len;
+ if (end - pos >= (int) sizeof(le16) &&
+ sae_group_allowed(sta->sae, groups,
+ WPA_GET_LE16(pos)) ==
+ WLAN_STATUS_SUCCESS) {
+ /* Do not waste resources deriving the same PWE
+ * again since the same group is reused. */
+ sae_set_state(sta, SAE_NOTHING,
+ "Allow previous PWE to be reused");
+ allow_reuse = 1;
+ } else {
+ sae_set_state(sta, SAE_NOTHING,
+ "Clear existing state to allow restart");
+ sae_clear_data(sta->sae);
+ }
+ }
+
+ resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+ ((const u8 *) mgmt) + len -
+ mgmt->u.auth.variable, &token,
+ &token_len, groups, status_code ==
+ WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK,
+ NULL);
+ if (resp == SAE_SILENTLY_DISCARD) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Drop commit message from " MACSTR " due to reflection attack",
+ MAC2STR(sta->addr));
+ goto remove_sta;
+ }
+
+ if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+ MACSTR, MAC2STR(sta->addr));
+ sae_clear_retransmit_timer(hapd, sta);
+ sae_set_state(sta, SAE_NOTHING,
+ "Unknown Password Identifier");
+ goto remove_sta;
+ }
+
+ if (token &&
+ check_comeback_token(hapd->comeback_key,
+ hapd->comeback_pending_idx, sta->addr,
+ token, token_len)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+ "incorrect token from " MACSTR,
+ MAC2STR(sta->addr));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto remove_sta;
+ }
+
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto reply;
+
+ if (check_sae_rejected_groups(hapd, sta->sae)) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+
+ if (!token && use_anti_clogging(hapd) && !allow_reuse) {
+ int h2e = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "SAE: Request anti-clogging token from "
+ MACSTR, MAC2STR(sta->addr));
+ if (sta->sae->tmp)
+ h2e = sta->sae->h2e;
+ if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK)
+ h2e = 1;
+ data = auth_build_token_req(
+ &hapd->last_comeback_key_update,
+ hapd->comeback_key,
+ hapd->comeback_idx,
+ hapd->comeback_pending_idx,
+ sizeof(hapd->comeback_pending_idx),
+ sta->sae->group,
+ sta->addr, h2e);
+ resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+ if (hapd->conf->mesh & MESH_ENABLED)
+ sae_set_state(sta, SAE_NOTHING,
+ "Request anti-clogging token case in mesh");
+ goto reply;
+ }
+
+ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ status_code, allow_reuse, &sta_removed);
+ } else if (auth_transaction == 2) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "SAE authentication (RX confirm, status=%u (%s))",
+ status_code, status2str(status_code));
+ if (status_code != WLAN_STATUS_SUCCESS)
+ goto remove_sta;
+ if (sta->sae->state >= SAE_CONFIRMED ||
+ !(hapd->conf->mesh & MESH_ENABLED)) {
+ const u8 *var;
+ size_t var_len;
+ u16 peer_send_confirm;
+
+ var = mgmt->u.auth.variable;
+ var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
+ if (var_len < 2) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+
+ peer_send_confirm = WPA_GET_LE16(var);
+
+ if (sta->sae->state == SAE_ACCEPTED &&
+ (peer_send_confirm <= sta->sae->rc ||
+ peer_send_confirm == 0xffff)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Silently ignore unexpected Confirm from peer "
+ MACSTR
+ " (peer-send-confirm=%u Rc=%u)",
+ MAC2STR(sta->addr),
+ peer_send_confirm, sta->sae->rc);
+ return;
+ }
+
+ if (sae_check_confirm(sta->sae, var, var_len,
+ NULL) < 0) {
+ resp = WLAN_STATUS_CHALLENGE_FAIL;
+ goto reply;
+ }
+ sta->sae->rc = peer_send_confirm;
+ }
+ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ status_code, 0, &sta_removed);
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "unexpected SAE authentication transaction %u (status=%u (%s))",
+ auth_transaction, status_code,
+ status2str(status_code));
+ if (status_code != WLAN_STATUS_SUCCESS)
+ goto remove_sta;
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ }
+
+reply:
+ if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + len;
+
+ /* Copy the Finite Cyclic Group field from the request if we
+ * rejected it as unsupported group. */
+ if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+ !data && end - pos >= 2)
+ data = wpabuf_alloc_copy(pos, 2);
+
+ sae_sme_send_external_auth_status(hapd, sta, resp);
+ send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp,
+ data ? wpabuf_head(data) : (u8 *) "",
+ data ? wpabuf_len(data) : 0, "auth-sae");
+ }
+
+remove_sta:
+ if (auth_transaction == 1)
+ success_status = sae_status_success(hapd, status_code);
+ else
+ success_status = status_code == WLAN_STATUS_SUCCESS;
+ if (!sta_removed && sta->added_unassoc &&
+ (resp != WLAN_STATUS_SUCCESS || !success_status)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+ wpabuf_free(data);
+}
+
+
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int ret;
+
+ if (!sta->sae || !sta->sae->tmp)
+ return -1;
+
+ if (sta->sae->state != SAE_NOTHING)
+ return -1;
+
+ ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+ if (ret)
+ return -1;
+
+ sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+
+ return 0;
+}
+
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct hostapd_sae_commit_queue *q;
+ unsigned int queue_len;
+
+ q = dl_list_first(&hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue, list);
+ if (!q)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "SAE: Process next available message from queue");
+ dl_list_del(&q->list);
+ handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
+ q->rssi, 1);
+ os_free(q);
+
+ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+ return;
+ queue_len = dl_list_len(&hapd->sae_commit_queue);
+ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+ hapd, NULL);
+}
+
+
+static void auth_sae_queue(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi)
+{
+ struct hostapd_sae_commit_queue *q, *q2;
+ unsigned int queue_len;
+ const struct ieee80211_mgmt *mgmt2;
+
+ queue_len = dl_list_len(&hapd->sae_commit_queue);
+ if (queue_len >= 15) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No more room in message queue - drop the new frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
+ MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
+ queue_len);
+ q = os_zalloc(sizeof(*q) + len);
+ if (!q)
+ return;
+ q->rssi = rssi;
+ q->len = len;
+ os_memcpy(q->msg, mgmt, len);
+
+ /* Check whether there is already a queued Authentication frame from the
+ * same station with the same transaction number and if so, replace that
+ * queue entry with the new one. This avoids issues with a peer that
+ * sends multiple times (e.g., due to frequent SAE retries). There is no
+ * point in us trying to process the old attempts after a new one has
+ * obsoleted them. */
+ dl_list_for_each(q2, &hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue, list) {
+ mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
+ if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+ mgmt->u.auth.auth_transaction ==
+ mgmt2->u.auth.auth_transaction) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Replace queued message from same STA with same transaction number");
+ dl_list_add(&q2->list, &q->list);
+ dl_list_del(&q2->list);
+ os_free(q2);
+ goto queued;
+ }
+ }
+
+ /* No pending identical entry, so add to the end of the queue */
+ dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
+
+queued:
+ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+ return;
+ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+ hapd, NULL);
+}
+
+
+static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct hostapd_sae_commit_queue *q;
+ const struct ieee80211_mgmt *mgmt;
+
+ dl_list_for_each(q, &hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue, list) {
+ mgmt = (const struct ieee80211_mgmt *) q->msg;
+ if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_SAE */
+
+
+static u16 wpa_res_to_status_code(enum wpa_validate_result res)
+{
+ switch (res) {
+ case WPA_IE_OK:
+ return WLAN_STATUS_SUCCESS;
+ case WPA_INVALID_IE:
+ return WLAN_STATUS_INVALID_IE;
+ case WPA_INVALID_GROUP:
+ return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ case WPA_INVALID_PAIRWISE:
+ return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ case WPA_INVALID_AKMP:
+ return WLAN_STATUS_AKMP_NOT_VALID;
+ case WPA_NOT_ENABLED:
+ return WLAN_STATUS_INVALID_IE;
+ case WPA_ALLOC_FAIL:
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
+ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ case WPA_INVALID_MGMT_GROUP_CIPHER:
+ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ case WPA_INVALID_MDIE:
+ return WLAN_STATUS_INVALID_MDIE;
+ case WPA_INVALID_PROTO:
+ return WLAN_STATUS_INVALID_IE;
+ case WPA_INVALID_PMKID:
+ return WLAN_STATUS_INVALID_PMKID;
+ case WPA_DENIED_OTHER_REASON:
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+ return WLAN_STATUS_INVALID_IE;
+}
+
+
+#ifdef CONFIG_FILS
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub);
+
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, size_t len, u16 auth_alg,
+ u16 auth_transaction, u16 status_code,
+ void (*cb)(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub))
+{
+ u16 resp = WLAN_STATUS_SUCCESS;
+ const u8 *end;
+ struct ieee802_11_elems elems;
+ enum wpa_validate_result res;
+ struct wpa_ie_data rsn;
+ struct rsn_pmksa_cache_entry *pmksa = NULL;
+
+ if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+ return;
+
+ end = pos + len;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+ pos, end - pos);
+
+ /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+ if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
+ u16 group;
+ struct wpabuf *pub;
+ size_t elem_len;
+
+ /* Using FILS PFS */
+
+ /* Finite Cyclic Group */
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No room for Finite Cyclic Group");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+ if (group != hapd->conf->fils_dh_group) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
+ group, hapd->conf->fils_dh_group);
+ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ sta->fils_ecdh = crypto_ecdh_init(group);
+ if (!sta->fils_ecdh) {
+ wpa_printf(MSG_INFO,
+ "FILS: Could not initialize ECDH with group %d",
+ group);
+ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+ if (!pub) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to derive ECDH public key");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ elem_len = wpabuf_len(pub);
+ wpabuf_free(pub);
+
+ /* Element */
+ if ((size_t) (end - pos) < elem_len) {
+ wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ wpabuf_free(sta->fils_g_sta);
+ sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
+ pos, elem_len);
+ if (!sta->fils_dh_ss) {
+ wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
+ pos += elem_len;
+ } else {
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ sta->fils_ecdh = NULL;
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = NULL;
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+ if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ /* RSNE */
+ wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
+ elems.rsn_ie, elems.rsn_ie_len);
+ if (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (!sta->wpa_sm)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
+ NULL);
+ if (!sta->wpa_sm) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to initialize RSN state machine");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
+ elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ elems.rsnxe ? elems.rsnxe - 2 : NULL,
+ elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+ elems.mdie, elems.mdie_len, NULL, 0);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ if (!elems.fils_nonce) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
+ FILS_NONCE_LEN);
+ os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+ /* PMKID List */
+ if (rsn.pmkid && rsn.num_pmkid > 0) {
+ u8 num;
+ const u8 *pmkid;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+ rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+ pmkid = rsn.pmkid;
+ num = rsn.num_pmkid;
+ while (num) {
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
+ pmkid);
+ if (pmksa)
+ break;
+ pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
+ sta->addr,
+ pmkid);
+ if (pmksa)
+ break;
+ pmkid += PMKID_LEN;
+ num--;
+ }
+ }
+ if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
+ wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
+ pmksa = NULL;
+ }
+ if (pmksa)
+ wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
+
+ /* FILS Session */
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+ os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
+
+ /* Wrapped Data */
+ if (elems.wrapped_data) {
+ wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+ elems.wrapped_data,
+ elems.wrapped_data_len);
+ if (!pmksa) {
+#ifndef CONFIG_NO_RADIUS
+ if (!sta->eapol_sm) {
+ sta->eapol_sm =
+ ieee802_1x_alloc_eapol_sm(hapd, sta);
+ }
+ wpa_printf(MSG_DEBUG,
+ "FILS: Forward EAP-Initiate/Re-auth to authentication server");
+ ieee802_1x_encapsulate_radius(
+ hapd, sta, elems.wrapped_data,
+ elems.wrapped_data_len);
+ sta->fils_pending_cb = cb;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Will send Authentication frame once the response from authentication server is available");
+ sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+ /* Calculate pending PMKID here so that we do not need
+ * to maintain a copy of the EAP-Initiate/Reauth
+ * message. */
+ if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ elems.wrapped_data,
+ elems.wrapped_data_len,
+ sta->fils_erp_pmkid) == 0)
+ sta->fils_erp_pmkid_set = 1;
+ return;
+#else /* CONFIG_NO_RADIUS */
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+#endif /* CONFIG_NO_RADIUS */
+ }
+ }
+
+fail:
+ if (cb) {
+ struct wpabuf *data;
+ int pub = 0;
+
+ data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
+ NULL, 0, &pub);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "%s: prepare_auth_resp_fils() returned failure",
+ __func__);
+ }
+
+ cb(hapd, sta, resp, data, pub);
+ }
+}
+
+
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 *resp,
+ struct rsn_pmksa_cache_entry *pmksa,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len,
+ int *is_pub)
+{
+ u8 fils_nonce[FILS_NONCE_LEN];
+ size_t ielen;
+ struct wpabuf *data = NULL;
+ const u8 *ie;
+ u8 *ie_buf = NULL;
+ const u8 *pmk = NULL;
+ size_t pmk_len = 0;
+ u8 pmk_buf[PMK_LEN_MAX];
+ struct wpabuf *pub = NULL;
+
+ if (*resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+ if (!ie) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (pmksa) {
+ /* Add PMKID of the selected PMKSA into RSNE */
+ ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
+ if (!ie_buf) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ os_memcpy(ie_buf, ie, ielen);
+ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ ie = ie_buf;
+ }
+
+ if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
+ fils_nonce, FILS_NONCE_LEN);
+
+#ifdef CONFIG_FILS_SK_PFS
+ if (sta->fils_dh_ss && sta->fils_ecdh) {
+ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+ if (!pub) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
+ if (!data) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+ if (pub) {
+ /* Finite Cyclic Group */
+ wpabuf_put_le16(data, hapd->conf->fils_dh_group);
+
+ /* Element */
+ wpabuf_put_buf(data, pub);
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ /* RSNE */
+ wpabuf_put_data(data, ie, ielen);
+
+ /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
+ /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
+ int res;
+
+ res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
+ wpabuf_put(data, 0),
+ wpabuf_tailroom(data));
+ if (res < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpabuf_put(data, res);
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ /* FILS Nonce */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
+
+ /* Wrapped Data */
+ if (!pmksa && erp_resp) {
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA);
+ wpabuf_put_buf(data, erp_resp);
+
+ if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ msk, msk_len, sta->fils_snonce, fils_nonce,
+ sta->fils_dh_ss ?
+ wpabuf_head(sta->fils_dh_ss) : NULL,
+ sta->fils_dh_ss ?
+ wpabuf_len(sta->fils_dh_ss) : 0,
+ pmk_buf, &pmk_len)) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(data);
+ data = NULL;
+ goto fail;
+ }
+ pmk = pmk_buf;
+
+ /* Don't use DHss in PTK derivation if PMKSA caching is not
+ * used. */
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = NULL;
+
+ if (sta->fils_erp_pmkid_set) {
+ /* TODO: get PMKLifetime from WPA parameters */
+ unsigned int dot11RSNAConfigPMKLifetime = 43200;
+ int session_timeout;
+
+ session_timeout = dot11RSNAConfigPMKLifetime;
+ if (sta->session_timeout_set) {
+ struct os_reltime now, diff;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now,
+ &diff);
+ session_timeout = diff.sec;
+ }
+
+ sta->fils_erp_pmkid_set = 0;
+ wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
+ sta->fils_erp_pmkid);
+ if (!hapd->conf->disable_pmksa_caching &&
+ wpa_auth_pmksa_add2(
+ hapd->wpa_auth, sta->addr,
+ pmk, pmk_len,
+ sta->fils_erp_pmkid,
+ session_timeout,
+ wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to add PMKSA cache entry based on ERP");
+ }
+ }
+ } else if (pmksa) {
+ pmk = pmksa->pmk;
+ pmk_len = pmksa->pmk_len;
+ }
+
+ if (!pmk) {
+ wpa_printf(MSG_DEBUG, "FILS: No PMK available");
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(data);
+ data = NULL;
+ goto fail;
+ }
+
+ if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
+ sta->fils_snonce, fils_nonce,
+ sta->fils_dh_ss ?
+ wpabuf_head(sta->fils_dh_ss) : NULL,
+ sta->fils_dh_ss ?
+ wpabuf_len(sta->fils_dh_ss) : 0,
+ sta->fils_g_sta, pub) < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(data);
+ data = NULL;
+ goto fail;
+ }
+
+fail:
+ if (is_pub)
+ *is_pub = pub != NULL;
+ os_free(ie_buf);
+ wpabuf_free(pub);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = NULL;
+#ifdef CONFIG_FILS_SK_PFS
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ sta->fils_ecdh = NULL;
+#endif /* CONFIG_FILS_SK_PFS */
+ return data;
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub)
+{
+ u16 auth_alg;
+
+ auth_alg = (pub ||
+ resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
+ WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+ send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp,
+ data ? wpabuf_head(data) : (u8 *) "",
+ data ? wpabuf_len(data) : 0, "auth-fils-finish");
+ wpabuf_free(data);
+
+ if (resp == WLAN_STATUS_SUCCESS) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (FILS)");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+ mlme_authenticate_indication(hapd, sta);
+ }
+}
+
+
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len)
+{
+ u16 resp;
+ u32 flags = sta->flags;
+
+ sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP |
+ WLAN_STA_PENDING_PASN_FILS_ERP);
+
+ resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (flags & WLAN_STA_PENDING_FILS_ERP) {
+ struct wpabuf *data;
+ int pub = 0;
+
+ if (!sta->fils_pending_cb)
+ return;
+
+ data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
+ msk, msk_len, &pub);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "%s: prepare_auth_resp_fils() failure",
+ __func__);
+ }
+ sta->fils_pending_cb(hapd, sta, resp, data, pub);
+#ifdef CONFIG_PASN
+ } else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) {
+ pasn_fils_auth_resp(hapd, sta, resp, erp_resp,
+ msk, msk_len);
+#endif /* CONFIG_PASN */
+ }
+}
+
+#endif /* CONFIG_FILS */
+
+
+static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len,
+ struct radius_sta *info)
+{
+ int res;
+
+ res = hostapd_allowed_address(hapd, addr, msg, len, info, 0);
+
+ if (res == HOSTAPD_ACL_REJECT) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not allowed to authenticate",
+ MAC2STR(addr));
+ return HOSTAPD_ACL_REJECT;
+ }
+
+ if (res == HOSTAPD_ACL_PENDING) {
+ wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+ " waiting for an external authentication",
+ MAC2STR(addr));
+ /* Authentication code will re-send the authentication frame
+ * after it has received (and cached) information from the
+ * external source. */
+ return HOSTAPD_ACL_PENDING;
+ }
+
+ return res;
+}
+
+
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, struct radius_sta *info)
+{
+ u32 session_timeout = info->session_timeout;
+ u32 acct_interim_interval = info->acct_interim_interval;
+ struct vlan_description *vlan_id = &info->vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk = info->psk;
+ char *identity = info->identity;
+ char *radius_cui = info->radius_cui;
+
+ if (vlan_id->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ vlan_id->untagged,
+ vlan_id->tagged[0] ? "+" : "");
+ return -1;
+ }
+ if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
+ return -1;
+ if (sta->vlan_id)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+ hostapd_free_psk_list(sta->psk);
+ if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED)
+ hostapd_copy_psk_list(&sta->psk, psk);
+ else
+ sta->psk = NULL;
+
+ os_free(sta->identity);
+ if (identity)
+ sta->identity = os_strdup(identity);
+ else
+ sta->identity = NULL;
+
+ os_free(sta->radius_cui);
+ if (radius_cui)
+ sta->radius_cui = os_strdup(radius_cui);
+ else
+ sta->radius_cui = NULL;
+
+ if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+ sta->acct_interim_interval = acct_interim_interval;
+ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
+ sta->session_timeout_set = 1;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ } else {
+ sta->session_timeout_set = 0;
+ ap_sta_no_session_timeout(hapd, sta);
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_FILS
+
+static void pasn_fils_auth_resp(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 status,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len)
+{
+ struct pasn_data *pasn = sta->pasn;
+ struct pasn_fils *fils = &pasn->fils;
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u",
+ status);
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ if (!pasn->secret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret");
+ goto fail;
+ }
+
+ if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
+ fils->anonce, FILS_NONCE_LEN);
+
+ ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce,
+ fils->anonce, NULL, 0, pmk, &pmk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
+ goto fail;
+ }
+
+ ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
+ wpabuf_head(pasn->secret),
+ wpabuf_len(pasn->secret),
+ &sta->pasn->ptk, sta->pasn->akmp,
+ sta->pasn->cipher, sta->pasn->kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
+ goto fail;
+ }
+
+ if (pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Failed to derive LTF keyseed");
+ goto fail;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+
+ wpabuf_free(pasn->secret);
+ pasn->secret = NULL;
+
+ fils->erp_resp = erp_resp;
+ ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
+ WLAN_STATUS_SUCCESS);
+ fils->erp_resp = NULL;
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response");
+ goto fail;
+ }
+
+ fils->state = PASN_FILS_STATE_COMPLETE;
+ return;
+fail:
+ ap_free_sta(hapd, sta);
+}
+
+
+static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ struct wpabuf *wd)
+{
+#ifdef CONFIG_NO_RADIUS
+ wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail");
+ return -1;
+#else /* CONFIG_NO_RADIUS */
+ struct pasn_data *pasn = sta->pasn;
+ struct pasn_fils *fils = &pasn->fils;
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsne_data;
+ struct wpabuf *fils_wd;
+ const u8 *data;
+ size_t buf_len;
+ u16 alg, seq, status;
+ int ret;
+
+ if (fils->state != PASN_FILS_STATE_NONE) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data");
+ return -1;
+ }
+
+ if (!wd) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data");
+ return -1;
+ }
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%zu",
+ buf_len);
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_FILS_SK || seq != 1 ||
+ status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Dropping peer authentication");
+ return -1;
+ }
+
+ data += 6;
+ buf_len -= 6;
+
+ if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
+ return -1;
+ }
+
+ if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
+ !elems.wrapped_data || !elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
+ return -1;
+ }
+
+ ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
+ return -1;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
+ return -1;
+ }
+
+ if (rsne_data.num_pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Not expecting PMKID in RSNE");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce,
+ FILS_NONCE_LEN);
+ os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session,
+ FILS_SESSION_LEN);
+ os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
+
+ fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!fils_wd) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
+ return -1;
+ }
+
+ if (!sta->eapol_sm)
+ sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Forward EAP-Initiate/Re-auth to AS");
+
+ ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd),
+ wpabuf_len(fils_wd));
+
+ sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP;
+
+ fils->state = PASN_FILS_STATE_PENDING_AS;
+
+ /*
+ * Calculate pending PMKID here so that we do not need to maintain a
+ * copy of the EAP-Initiate/Reautt message.
+ */
+ fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd),
+ fils->erp_pmkid);
+
+ wpabuf_free(fils_wd);
+ return 0;
+#endif /* CONFIG_NO_RADIUS */
+}
+
+#endif /* CONFIG_FILS */
+
+
+static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, unsigned int wait)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
+}
+
+
+static void hapd_initialize_pasn(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct pasn_data *pasn = sta->pasn;
+
+ pasn->cb_ctx = hapd;
+ pasn->send_mgmt = hapd_pasn_send_mlme;
+ pasn->pasn_groups = hapd->conf->pasn_groups;
+ pasn->noauth = hapd->conf->pasn_noauth;
+ pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+ pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
+ pasn->derive_kdk = hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+#ifdef CONFIG_TESTING_OPTIONS
+ pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+ if (hapd->conf->force_kdk_derivation)
+ pasn->derive_kdk = true;
+#endif /* CONFIG_TESTING_OPTIONS */
+ pasn->use_anti_clogging = use_anti_clogging(hapd);
+ pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
+ NULL);
+ pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+ pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
+ pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+
+ pasn->comeback_after = hapd->conf->pasn_comeback_after;
+ pasn->comeback_idx = hapd->comeback_idx;
+ pasn->comeback_key = hapd->comeback_key;
+ pasn->comeback_pending_idx = hapd->comeback_pending_idx;
+ os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
+}
+
+
+static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *sta_addr,
+ int cipher, int akmp)
+{
+ struct ptksa_cache_entry *entry;
+
+ entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
+ if (!entry) {
+ wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+ " not present in PTKSA cache", MAC2STR(sta_addr));
+ return -1;
+ }
+
+ if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: own addr " MACSTR " and PTKSA entry own addr "
+ MACSTR " differ",
+ MAC2STR(own_addr), MAC2STR(entry->own_addr));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+ MAC2STR(sta_addr));
+ hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
+ entry->ptk.tk_len, entry->ptk.tk,
+ entry->ptk.ltf_keyseed_len,
+ entry->ptk.ltf_keyseed, 0);
+
+ return 0;
+}
+
+
+static void hapd_pasn_update_params(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct pasn_data *pasn = sta->pasn;
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn_data;
+ struct wpa_pasn_params_data pasn_params;
+ struct wpabuf *wrapped_data = NULL;
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed parsing Authentication frame");
+ return;
+ }
+
+ if (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data)) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+ return;
+ }
+
+ if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+ !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ return;
+ }
+
+ pasn->akmp = rsn_data.key_mgmt;
+ pasn->cipher = rsn_data.pairwise_cipher;
+
+ if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+#ifdef CONFIG_IEEE80211R_AP
+ pasn->pmk_r1_len = 0;
+ wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
+ rsn_data.pmkid,
+ pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+#endif /* CONFIG_IEEE80211R_AP */
+ }
+#ifdef CONFIG_FILS
+ if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+ pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+ return;
+ if (!elems.pasn_params ||
+ wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ false, &pasn_params)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed validation of PASN Parameters element");
+ return;
+ }
+ if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+ wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+ if (!wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+ return;
+ }
+ if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing FILS wrapped data");
+ else
+ pasn->fils_wd_valid = true;
+ }
+ wpabuf_free(wrapped_data);
+#endif /* CONFIG_FILS */
+}
+
+
+static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ u16 trans_seq, u16 status)
+{
+ if (hapd->conf->wpa != WPA_PROTO_RSN) {
+ wpa_printf(MSG_INFO, "PASN: RSN is not configured");
+ return;
+ }
+
+ wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
+ MAC2STR(sta->addr));
+
+ if (trans_seq == 1) {
+ if (sta->pasn) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Not expecting transaction == 1");
+ return;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failure status in transaction == 1");
+ return;
+ }
+
+ sta->pasn = os_zalloc(sizeof(*sta->pasn));
+ if (!sta->pasn) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to allocate PASN context");
+ return;
+ }
+
+ hapd_initialize_pasn(hapd, sta);
+
+ hapd_pasn_update_params(hapd, sta, mgmt, len);
+ if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
+ sta->addr, mgmt, len) < 0)
+ ap_free_sta(hapd, sta);
+ } else if (trans_seq == 3) {
+ if (!sta->pasn) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Not expecting transaction == 3");
+ return;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failure status in transaction == 3");
+ ap_free_sta_pasn(hapd, sta);
+ return;
+ }
+
+ if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
+ sta->addr, mgmt, len) == 0) {
+ ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+ sta->pasn->cipher, 43200,
+ &sta->pasn->ptk, NULL, NULL,
+ sta->pasn->akmp);
+
+ pasn_set_keys_from_cache(hapd, hapd->own_addr,
+ sta->addr, sta->pasn->cipher,
+ sta->pasn->akmp);
+ }
+ ap_free_sta(hapd, sta);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid transaction %u - ignore", trans_seq);
+ }
+}
+
+#endif /* CONFIG_PASN */
+
+
+static void handle_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi, int from_queue)
+{
+ u16 auth_alg, auth_transaction, status_code;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+ int res, reply_res, ubus_resp;
+ u16 fc;
+ const u8 *challenge = NULL;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+ size_t resp_ies_len = 0;
+ u16 seq_ctrl;
+ struct radius_sta rad_info;
+ const u8 *dst, *sa, *bssid;
+ bool mld_sta = false;
+ struct hostapd_ubus_request req = {
+ .type = HOSTAPD_UBUS_AUTH_REQ,
+ .mgmt_frame = mgmt,
+ .ssi_signal = rssi,
+ };
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iconf->ignore_auth_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_auth_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring auth frame from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ sa = mgmt->sa;
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Handle MLO authentication before the station is added to hostapd and
+ * the driver so that the station MLD MAC address would be used in both
+ * hostapd and the driver.
+ */
+ sa = hostapd_process_ml_auth(hapd, mgmt, len);
+ if (sa)
+ mld_sta = true;
+ else
+ sa = mgmt->sa;
+#endif /* CONFIG_IEEE80211BE */
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ status_code = le_to_host16(mgmt->u.auth.status_code);
+ fc = le_to_host16(mgmt->frame_control);
+ seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+ if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
+ 2 + WLAN_AUTH_CHALLENGE_LEN &&
+ mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
+ mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
+ challenge = &mgmt->u.auth.variable[2];
+
+ wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
+ "auth_transaction=%d status_code=%d wep=%d%s "
+ "seq_ctrl=0x%x%s%s",
+ MAC2STR(sa), auth_alg, auth_transaction,
+ status_code, !!(fc & WLAN_FC_ISWEP),
+ challenge ? " challenge" : "",
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
+ from_queue ? " (from queue)" : "");
+
+#ifdef CONFIG_NO_RC4
+ if (auth_alg == WLAN_AUTH_SHARED_KEY) {
+ wpa_printf(MSG_INFO,
+ "Unsupported authentication algorithm (%d)",
+ auth_alg);
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+#endif /* CONFIG_NO_RC4 */
+
+ if (hapd->tkip_countermeasures) {
+ wpa_printf(MSG_DEBUG,
+ "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
+ auth_alg == WLAN_AUTH_OPEN) ||
+#ifdef CONFIG_IEEE80211R_AP
+ (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_FT) ||
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+ (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_SAE) ||
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_FILS_SK) ||
+ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+ hapd->conf->fils_dh_group &&
+ auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_PASN
+ (hapd->conf->wpa &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
+ auth_alg == WLAN_AUTH_PASN) ||
+#endif /* CONFIG_PASN */
+ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
+ auth_alg == WLAN_AUTH_SHARED_KEY))) {
+ wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
+ auth_alg);
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+
+ if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
+#ifdef CONFIG_PASN
+ (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
+#endif /* CONFIG_PASN */
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
+ wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
+ auth_transaction);
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto fail;
+ }
+
+ if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (mld_sta &&
+ (os_memcmp(sa, hapd->own_addr, ETH_ALEN) == 0 ||
+ os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) {
+ wpa_printf(MSG_INFO,
+ "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (hapd->conf->no_auth_if_seen_on) {
+ struct hostapd_data *other;
+
+ other = sta_track_seen_on(hapd->iface, sa,
+ hapd->conf->no_auth_if_seen_on);
+ if (other) {
+ u8 *pos;
+ u32 info;
+ u8 op_class, channel, phytype;
+
+ wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
+ MACSTR " since STA has been seen on %s",
+ hapd->conf->iface, MAC2STR(sa),
+ hapd->conf->no_auth_if_seen_on);
+
+ resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
+ pos = &resp_ies[0];
+ *pos++ = WLAN_EID_NEIGHBOR_REPORT;
+ *pos++ = 13;
+ os_memcpy(pos, other->own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ info = 0; /* TODO: BSSID Information */
+ WPA_PUT_LE32(pos, info);
+ pos += 4;
+ if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+ phytype = 8; /* dmg */
+ else if (other->iconf->ieee80211ac)
+ phytype = 9; /* vht */
+ else if (other->iconf->ieee80211n)
+ phytype = 7; /* ht */
+ else if (other->iconf->hw_mode ==
+ HOSTAPD_MODE_IEEE80211A)
+ phytype = 4; /* ofdm */
+ else if (other->iconf->hw_mode ==
+ HOSTAPD_MODE_IEEE80211G)
+ phytype = 6; /* erp */
+ else
+ phytype = 5; /* hrdsss */
+ if (ieee80211_freq_to_channel_ext(
+ hostapd_hw_get_freq(other,
+ other->iconf->channel),
+ other->iconf->secondary_channel,
+ other->iconf->ieee80211ac,
+ &op_class, &channel) == NUM_HOSTAPD_MODES) {
+ op_class = 0;
+ channel = other->iconf->channel;
+ }
+ *pos++ = op_class;
+ *pos++ = channel;
+ *pos++ = phytype;
+ resp_ies_len = pos - &resp_ies[0];
+ goto fail;
+ }
+ }
+
+ res = ieee802_11_allowed_address(hapd, sa, (const u8 *) mgmt, len,
+ &rad_info);
+ if (res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Authentication frame from " MACSTR
+ " due to ACL reject", MAC2STR(sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
+ if (ubus_resp) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
+ MAC2STR(mgmt->sa));
+ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (res == HOSTAPD_ACL_PENDING)
+ return;
+
+#ifdef CONFIG_SAE
+ if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
+ (auth_transaction == 1 ||
+ (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
+ /* Handle SAE Authentication commit message through a queue to
+ * provide more control for postponing the needed heavy
+ * processing under a possible DoS attack scenario. In addition,
+ * queue SAE Authentication confirm message if there happens to
+ * be a queued commit message from the same peer. This is needed
+ * to avoid reordering Authentication frames within the same
+ * SAE exchange. */
+ auth_sae_queue(hapd, mgmt, len, rssi);
+ return;
+ }
+#endif /* CONFIG_SAE */
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta) {
+ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+ sta->ft_over_ds = 0;
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+ sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated authentication frame seq_ctrl=0x%x",
+ seq_ctrl);
+ return;
+ }
+#ifdef CONFIG_PASN
+ if (auth_alg == WLAN_AUTH_PASN &&
+ (sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: auth: Existing station: " MACSTR,
+ MAC2STR(sta->addr));
+ return;
+ }
+#endif /* CONFIG_PASN */
+ } else {
+#ifdef CONFIG_MESH
+ if (hapd->conf->mesh & MESH_ENABLED) {
+ /* if the mesh peer is not available, we don't do auth.
+ */
+ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+ " not yet known - drop Authentication frame",
+ MAC2STR(sa));
+ /*
+ * Save a copy of the frame so that it can be processed
+ * if a new peer entry is added shortly after this.
+ */
+ wpabuf_free(hapd->mesh_pending_auth);
+ hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
+ os_get_reltime(&hapd->mesh_pending_auth_time);
+ return;
+ }
+#endif /* CONFIG_MESH */
+
+ sta = ap_sta_add(hapd, sa);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (auth_transaction == 1) {
+ os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
+
+ if (mld_sta) {
+ u8 link_id = hapd->mld_link_id;
+
+ sta->mld_info.mld_sta = true;
+ sta->mld_assoc_link_id = link_id;
+
+ /*
+ * Set the MLD address as the station address and the
+ * station addresses.
+ */
+ os_memcpy(sta->mld_info.common_info.mld_addr, sa,
+ ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].peer_addr,
+ mgmt->sa, ETH_ALEN);
+ os_memcpy(sta->mld_info.links[link_id].local_addr,
+ hapd->own_addr, ETH_ALEN);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = WLAN_FC_STYPE_AUTH;
+#ifdef CONFIG_MBO
+ sta->auth_rssi = rssi;
+#endif /* CONFIG_MBO */
+
+ res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+ /*
+ * If the driver supports full AP client state, add a station to the
+ * driver before sending authentication reply to make sure the driver
+ * has resources, and not to go through the entire authentication and
+ * association handshake, and fail it at the end.
+ *
+ * If this is not the first transaction, in a multi-step authentication
+ * algorithm, the station already exists in the driver
+ * (sta->added_unassoc = 1) so skip it.
+ *
+ * In mesh mode, the station was already added to the driver when the
+ * NEW_PEER_CANDIDATE event is received.
+ *
+ * If PMF was negotiated for the existing association, skip this to
+ * avoid dropping the STA entry and the associated keys. This is needed
+ * to allow the original connection work until the attempt can complete
+ * (re)association, so that unprotected Authentication frame cannot be
+ * used to bypass PMF protection.
+ *
+ * PASN authentication does not require adding/removing station to the
+ * driver so skip this flow in case of PASN authentication.
+ */
+ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+ (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
+ !(hapd->conf->mesh & MESH_ENABLED) &&
+ !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) {
+ if (ap_sta_re_add(hapd, sta) < 0) {
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+ }
+
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (open system)");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_OPEN;
+ mlme_authenticate_indication(hapd, sta);
+ break;
+#ifdef CONFIG_WEP
+#ifndef CONFIG_NO_RC4
+ case WLAN_AUTH_SHARED_KEY:
+ resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+ fc & WLAN_FC_ISWEP);
+ if (resp != 0)
+ wpa_printf(MSG_DEBUG,
+ "auth_shared_key() failed: status=%d", resp);
+ sta->auth_alg = WLAN_AUTH_SHARED_KEY;
+ mlme_authenticate_indication(hapd, sta);
+ if (sta->challenge && auth_transaction == 1) {
+ resp_ies[0] = WLAN_EID_CHALLENGE;
+ resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
+ os_memcpy(resp_ies + 2, sta->challenge,
+ WLAN_AUTH_CHALLENGE_LEN);
+ resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
+ }
+ break;
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_WEP */
+#ifdef CONFIG_IEEE80211R_AP
+ case WLAN_AUTH_FT:
+ sta->auth_alg = WLAN_AUTH_FT;
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+ "state machine");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+ auth_transaction, mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN -
+ sizeof(mgmt->u.auth),
+ handle_auth_ft_finish, hapd);
+ /* handle_auth_ft_finish() callback will complete auth. */
+ return;
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+ case WLAN_AUTH_SAE:
+#ifdef CONFIG_MESH
+ if (status_code == WLAN_STATUS_SUCCESS &&
+ hapd->conf->mesh & MESH_ENABLED) {
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm =
+ wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Failed to initialize WPA state machine");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_MESH */
+ handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
+ status_code);
+ return;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ case WLAN_AUTH_FILS_SK:
+ case WLAN_AUTH_FILS_SK_PFS:
+ handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+ auth_alg, auth_transaction, status_code,
+ handle_auth_fils_finish);
+ return;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_PASN
+ case WLAN_AUTH_PASN:
+ handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
+ status_code);
+ return;
+#endif /* CONFIG_PASN */
+ }
+
+ fail:
+ dst = mgmt->sa;
+ bssid = mgmt->bssid;
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once a non-AP MLD is added to the driver, the addressing should use
+ * the MLD MAC address. It is the responsibility of the driver to
+ * handle the translations.
+ */
+ if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ dst = sta->addr;
+ bssid = hapd->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ reply_res = send_auth_reply(hapd, sta, dst, bssid, auth_alg,
+ auth_alg == WLAN_AUTH_SAE ?
+ auth_transaction : auth_transaction + 1,
+ resp, resp_ies, resp_ies_len,
+ "handle-auth");
+
+ if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
+ reply_res != WLAN_STATUS_SUCCESS)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+}
+
+
+static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
+{
+ size_t num_bss_nontx;
+ u8 max_bssid_ind = 0;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
+ return 0;
+
+ num_bss_nontx = hapd->iface->num_bss - 1;
+ while (num_bss_nontx > 0) {
+ max_bssid_ind++;
+ num_bss_nontx >>= 1;
+ }
+ return max_bssid_ind;
+}
+
+
+static u32 hostapd_get_aid_word(struct hostapd_data *hapd,
+ struct sta_info *sta, int i)
+{
+#ifdef CONFIG_IEEE80211BE
+ u32 aid_word = 0;
+
+ /* Do not assign an AID that is in use on any of the affiliated links
+ * when finding an AID for a non-AP MLD. */
+ if (hapd->conf->mld_ap) {
+ int j;
+
+ for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
+ struct hostapd_data *link_bss;
+
+ if (!sta->mld_info.links[j].valid)
+ continue;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, j);
+ if (!link_bss) {
+ /* This shouldn't happen, just skip */
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for AID");
+ continue;
+ }
+
+ aid_word |= link_bss->sta_aid[i];
+ }
+
+ return aid_word;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->sta_aid[i];
+}
+
+
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int i, j = 32, aid;
+
+ /* get a unique AID */
+ if (sta->aid > 0) {
+ wpa_printf(MSG_DEBUG, " old AID %d", sta->aid);
+ return 0;
+ }
+
+ if (TEST_FAIL())
+ return -1;
+
+ for (i = 0; i < AID_WORDS; i++) {
+ u32 aid_word = hostapd_get_aid_word(hapd, sta, i);
+
+ if (aid_word == (u32) -1)
+ continue;
+ for (j = 0; j < 32; j++) {
+ if (!(aid_word & BIT(j)))
+ break;
+ }
+ if (j < 32)
+ break;
+ }
+ if (j == 32)
+ return -1;
+ aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
+ if (aid > 2007)
+ return -1;
+
+ sta->aid = aid;
+ hapd->sta_aid[i] |= BIT(j);
+ wpa_printf(MSG_DEBUG, " new AID %d", sta->aid);
+ return 0;
+}
+
+
+static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ssid_ie, size_t ssid_ie_len)
+{
+ if (ssid_ie == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
+ os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to associate with unknown SSID "
+ "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *wmm_ie, size_t wmm_ie_len)
+{
+ sta->flags &= ~WLAN_STA_WMM;
+ sta->qosinfo = 0;
+ if (wmm_ie && hapd->conf->wmm_enabled) {
+ struct wmm_information_element *wmm;
+
+ if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "invalid WMM element in association "
+ "request");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_WMM;
+ wmm = (struct wmm_information_element *) wmm_ie;
+ sta->qosinfo = wmm->qos_info;
+ }
+ return WLAN_STATUS_SUCCESS;
+}
+
+static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *multi_ap_ie, size_t multi_ap_len)
+{
+ u8 multi_ap_value = 0;
+
+ sta->flags &= ~WLAN_STA_MULTI_AP;
+
+ if (!hapd->conf->multi_ap)
+ return WLAN_STATUS_SUCCESS;
+
+ if (multi_ap_ie) {
+ const u8 *multi_ap_subelem;
+
+ multi_ap_subelem = get_ie(multi_ap_ie + 4,
+ multi_ap_len - 4,
+ MULTI_AP_SUB_ELEM_TYPE);
+ if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
+ multi_ap_value = multi_ap_subelem[2];
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE has missing or invalid Multi-AP subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ }
+
+ if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE with unexpected value 0x%02x",
+ multi_ap_value);
+
+ if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ return WLAN_STATUS_SUCCESS;
+
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+
+ if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Backhaul STA tries to associate with fronthaul-only BSS");
+
+ sta->flags |= WLAN_STA_MULTI_AP;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ /* Supported rates not used in IEEE 802.11ad/DMG */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+ return WLAN_STATUS_SUCCESS;
+
+ if (!elems->supp_rates) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "No supported rates element in AssocReq");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (elems->supp_rates_len + elems->ext_supp_rates_len >
+ sizeof(sta->supported_rates)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Invalid supported rates element length %d+%d",
+ elems->supp_rates_len,
+ elems->ext_supp_rates_len);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->supported_rates_len = merge_byte_arrays(
+ sta->supported_rates, sizeof(sta->supported_rates),
+ elems->supp_rates, elems->supp_rates_len,
+ elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+#ifdef CONFIG_OWE
+
+static int owe_group_supported(struct hostapd_data *hapd, u16 group)
+{
+ int i;
+ int *groups = hapd->conf->owe_groups;
+
+ if (group != 19 && group != 20 && group != 21)
+ return 0;
+
+ if (!groups)
+ return 1;
+
+ for (i = 0; groups[i] > 0; i++) {
+ if (groups[i] == group)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static u16 owe_process_assoc_req(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *owe_dh,
+ u8 owe_dh_len)
+{
+ struct wpabuf *secret, *pub, *hkey;
+ int res;
+ u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+ const char *info = "OWE Key Generation";
+ const u8 *addr[2];
+ size_t len[2];
+ u16 group;
+ size_t hash_len, prime_len;
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ group = WPA_GET_LE16(owe_dh);
+ if (!owe_group_supported(hapd, group)) {
+ wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ }
+ if (group == 19)
+ prime_len = 32;
+ else if (group == 20)
+ prime_len = 48;
+ else if (group == 21)
+ prime_len = 66;
+ else
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+ if (sta->owe_group == group && sta->owe_ecdh) {
+ /* This is a workaround for mac80211 behavior of retransmitting
+ * the Association Request frames multiple times if the link
+ * layer retries (i.e., seq# remains same) fail. The mac80211
+ * initiated retransmission will use a different seq# and as
+ * such, will go through duplicate detection. If we were to
+ * change our DH key for that attempt, there would be two
+ * different DH shared secrets and the STA would likely select
+ * the wrong one. */
+ wpa_printf(MSG_DEBUG,
+ "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again");
+ } else {
+ crypto_ecdh_deinit(sta->owe_ecdh);
+ sta->owe_ecdh = crypto_ecdh_init(group);
+ }
+ if (!sta->owe_ecdh)
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ sta->owe_group = group;
+
+ secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+ owe_dh_len - 2);
+ secret = wpabuf_zeropad(secret, prime_len);
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+ /* prk = HKDF-extract(C | A | group, z) */
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ /* PMKID = Truncate-128(Hash(C | A)) */
+ addr[0] = owe_dh + 2;
+ len[0] = owe_dh_len - 2;
+ addr[1] = wpabuf_head(pub);
+ len[1] = wpabuf_len(pub);
+ if (group == 19) {
+ res = sha256_vector(2, addr, len, pmkid);
+ hash_len = SHA256_MAC_LEN;
+ } else if (group == 20) {
+ res = sha384_vector(2, addr, len, pmkid);
+ hash_len = SHA384_MAC_LEN;
+ } else if (group == 21) {
+ res = sha512_vector(2, addr, len, pmkid);
+ hash_len = SHA512_MAC_LEN;
+ } else {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ pub = wpabuf_zeropad(pub, prime_len);
+ if (res < 0 || !pub) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+ if (!hkey) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+ wpabuf_put_buf(hkey, pub); /* A */
+ wpabuf_free(pub);
+ wpabuf_put_le16(hkey, group); /* group */
+ if (group == 19)
+ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 20)
+ res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 21)
+ res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ wpabuf_clear_free(hkey);
+ wpabuf_clear_free(secret);
+ if (res < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = os_malloc(hash_len);
+ if (!sta->owe_pmk) {
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (group == 19)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ else if (group == 20)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ else if (group == 21)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ if (res < 0) {
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ sta->owe_pmk_len = hash_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
+ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+ wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+ const u8 *rsn_ie, size_t rsn_ie_len,
+ const u8 *owe_dh, size_t owe_dh_len)
+{
+ struct wpa_ie_data data;
+ int res;
+
+ if (!rsn_ie || rsn_ie_len < 2) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
+ MAC2STR(peer));
+ return WLAN_STATUS_INVALID_IE;
+ }
+ rsn_ie -= 2;
+ rsn_ie_len += 2;
+
+ res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
+ " (res=%d)", MAC2STR(peer), res);
+ wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
+ return wpa_res_to_status_code(res);
+ }
+ if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Unexpected key mgmt 0x%x from " MACSTR,
+ (unsigned int) data.key_mgmt, MAC2STR(peer));
+ return WLAN_STATUS_AKMP_NOT_VALID;
+ }
+ if (!owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: No Diffie-Hellman Parameter element from "
+ MACSTR, MAC2STR(peer));
+ return WLAN_STATUS_AKMP_NOT_VALID;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 owe_process_rsn_ie(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *rsn_ie, size_t rsn_ie_len,
+ const u8 *owe_dh, size_t owe_dh_len)
+{
+ u16 status;
+ u8 *owe_buf, ie[256 * 2];
+ size_t ie_len = 0;
+ enum wpa_validate_result res;
+
+ if (!rsn_ie || rsn_ie_len < 2) {
+ wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
+ status = WLAN_STATUS_INVALID_IE;
+ goto end;
+ }
+
+ if (!sta->wpa_sm)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
+ NULL);
+ if (!sta->wpa_sm) {
+ wpa_printf(MSG_WARNING,
+ "OWE: Failed to initialize WPA state machine");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto end;
+ }
+ rsn_ie -= 2;
+ rsn_ie_len += 2;
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq, rsn_ie, rsn_ie_len,
+ NULL, 0, NULL, 0, owe_dh, owe_dh_len);
+ status = wpa_res_to_status_code(res);
+ if (status != WLAN_STATUS_SUCCESS)
+ goto end;
+ status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+ if (status != WLAN_STATUS_SUCCESS)
+ goto end;
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
+ NULL, 0);
+ if (!owe_buf) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto end;
+ }
+
+ if (sta->owe_ecdh) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto end;
+ }
+
+ /* OWE Diffie-Hellman Parameter element */
+ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+ */
+ WPA_PUT_LE16(owe_buf, sta->owe_group);
+ owe_buf += 2;
+ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+ owe_buf += wpabuf_len(pub);
+ wpabuf_free(pub);
+ sta->external_dh_updated = 1;
+ }
+ ie_len = owe_buf - ie;
+
+end:
+ wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
+ MACSTR, status, (unsigned int) ie_len,
+ MAC2STR(sta->addr));
+ hostapd_drv_update_dh_ie(hapd, sta->addr, status,
+ status == WLAN_STATUS_SUCCESS ? ie : NULL,
+ ie_len);
+
+ return status;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc)
+{
+ if ((sta->flags &
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+ return false;
+
+ if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+
+ if (!sta->sa_query_timed_out &&
+ (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA Query timeout
+ * has not been reached. Reject the association attempt
+ * temporarily and start SA Query, if one is not pending.
+ */
+ if (sta->sa_query_count == 0)
+ ap_sta_start_sa_query(hapd, sta);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ies, size_t ies_len,
+ struct ieee802_11_elems *elems, int reassoc,
+ bool link)
+{
+ int resp;
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *p2p_dev_addr = NULL;
+
+ resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ resp = check_ext_capab(hapd, sta, elems->ext_capab,
+ elems->ext_capab_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ resp = copy_supp_rates(hapd, sta, elems);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+ !(sta->flags & WLAN_STA_HT)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station does not support "
+ "mandatory HT PHY - reject association");
+ return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ }
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac) {
+ resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
+ !(sta->flags & WLAN_STA_VHT)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station does not support "
+ "mandatory VHT PHY - reject association");
+ return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+ }
+
+ if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
+ resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
+ elems->vendor_vht_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
+ elems->he_capabilities,
+ elems->he_capabilities_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory HE PHY - reject association");
+ return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ }
+
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ if (!(sta->flags & WLAN_STA_HE)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory HE PHY - reject association");
+ return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ }
+ resp = copy_sta_he_6ghz_capab(hapd, sta,
+ elems->he_6ghz_band_cap);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+ }
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
+ elems->he_capabilities,
+ elems->he_capabilities_len,
+ elems->eht_capabilities,
+ elems->eht_capabilities_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ if (!link) {
+ resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_P2P
+ if (elems->p2p && ies && ies_len) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (sta->p2p_ie)
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ } else {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = NULL;
+ }
+#endif /* CONFIG_P2P */
+
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) {
+ wpa_ie = elems->rsn_ie;
+ wpa_ie_len = elems->rsn_ie_len;
+ } else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
+ elems->wpa_ie) {
+ wpa_ie = elems->wpa_ie;
+ wpa_ie_len = elems->wpa_ie_len;
+ } else {
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ }
+
+#ifdef CONFIG_WPS
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+ if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) {
+ wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
+ "Request - assume WPS is used");
+ if (check_sa_query(hapd, sta, reassoc))
+ return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+ sta->flags |= WLAN_STA_WPS;
+ wpabuf_free(sta->wps_ie);
+ sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ WPS_IE_VENDOR_TYPE);
+ if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
+ "(Re)Association Request - reject");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ } else if (hapd->conf->wps_state && wpa_ie == NULL) {
+ wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
+ "(Re)Association Request - possible WPS use");
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ } else
+#endif /* CONFIG_WPS */
+ if (hapd->conf->wpa && wpa_ie == NULL) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No WPA/RSN IE in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ if (hapd->conf->wpa && wpa_ie) {
+ enum wpa_validate_result res;
+
+ if (check_sa_query(hapd, sta, reassoc))
+ return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+ wpa_ie -= 2;
+ wpa_ie_len += 2;
+
+ if (!sta->wpa_sm) {
+#ifdef CONFIG_IEEE80211BE
+ struct mld_info *info = &sta->mld_info;
+#endif /* CONFIG_IEEE80211BE */
+
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr,
+ p2p_dev_addr);
+
+ if (!sta->wpa_sm) {
+ wpa_printf(MSG_WARNING,
+ "Failed to initialize RSN state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (info->mld_sta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Set ML info in RSN Authenticator");
+ wpa_auth_set_ml_info(sta->wpa_sm,
+ hapd->mld_addr,
+ sta->mld_assoc_link_id,
+ info);
+ }
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
+ wpa_ie, wpa_ie_len,
+ elems->rsnxe ? elems->rsnxe - 2 :
+ NULL,
+ elems->rsnxe ? elems->rsnxe_len + 2 :
+ 0,
+ elems->mdie, elems->mdie_len,
+ elems->owe_dh, elems->owe_dh_len);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
+ if (wpa_auth_uses_mfp(sta->wpa_sm))
+ sta->flags |= WLAN_STA_MFP;
+ else
+ sta->flags &= ~WLAN_STA_MFP;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ if (!reassoc) {
+ wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
+ "to use association (not "
+ "re-association) with FT auth_alg",
+ MAC2STR(sta->addr));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
+ ies_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if (link)
+ goto skip_sae_owe;
+#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
+ sta->sae->state == SAE_ACCEPTED)
+ wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+
+ if (wpa_auth_uses_sae(sta->wpa_sm) &&
+ sta->auth_alg == WLAN_AUTH_OPEN) {
+ struct rsn_pmksa_cache_entry *sa;
+ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No PMKSA cache entry found for "
+ MACSTR, MAC2STR(sta->addr));
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+ wpa_printf(MSG_DEBUG, "SAE: " MACSTR
+ " using PMKSA caching", MAC2STR(sta->addr));
+ } else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+ sta->auth_alg != WLAN_AUTH_SAE &&
+ !(sta->auth_alg == WLAN_AUTH_FT &&
+ wpa_auth_uses_ft_sae(sta->wpa_sm))) {
+ wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
+ "SAE AKM after non-SAE auth_alg %u",
+ MAC2STR(sta->addr), sta->auth_alg);
+ return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ }
+
+ if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
+ sta->auth_alg == WLAN_AUTH_SAE &&
+ sta->sae && !sta->sae->h2e &&
+ ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_INFO, "SAE: " MACSTR
+ " indicates support for SAE H2E, but did not use it",
+ MAC2STR(sta->addr));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems->owe_dh) {
+ resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
+ elems->owe_dh_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_OWE */
+ skip_sae_owe:
+
+#ifdef CONFIG_DPP2
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+
+ if (DPP_VERSION > 1 &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
+ elems->owe_dh) {
+ sta->dpp_pfs = dpp_pfs_init(
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey));
+ if (!sta->dpp_pfs) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not initialize PFS");
+ /* Try to continue without PFS */
+ goto pfs_fail;
+ }
+
+ if (dpp_pfs_process(sta->dpp_pfs, elems->owe_dh,
+ elems->owe_dh_len) < 0) {
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+
+ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+ sta->dpp_pfs->secret : NULL);
+ pfs_fail:
+#endif /* CONFIG_DPP2 */
+
+ if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
+ wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to use TKIP with HT "
+ "association");
+ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ }
+#ifdef CONFIG_HS20
+ } else if (hapd->conf->osen) {
+ if (!elems->osen) {
+ hostapd_logger(
+ hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No HS 2.0 OSEN element in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+ elems->osen - 2, elems->osen_len + 2) < 0)
+ return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
+ } else
+ wpa_auth_sta_no_wpa(sta->wpa_sm);
+
+#ifdef CONFIG_P2P
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+ wpabuf_free(sta->hs20_ie);
+ if (elems->hs20 && elems->hs20_len > 4) {
+ int release;
+
+ sta->hs20_ie = wpabuf_alloc_copy(elems->hs20 + 4,
+ elems->hs20_len - 4);
+ release = ((elems->hs20[4] >> 4) & 0x0f) + 1;
+ if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
+ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: PMF not negotiated by release %d station "
+ MACSTR, release, MAC2STR(sta->addr));
+ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ }
+ } else {
+ sta->hs20_ie = NULL;
+ }
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems->roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems->roaming_cons_sel + 4,
+ elems->roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+ if (hapd->iface->fst)
+ sta->mb_ies = mb_ies_by_info(&elems->mb_ies);
+ else
+ sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+ mbo_ap_check_sta_assoc(hapd, sta, elems);
+
+ if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+ elems->mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+ hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ wpa_printf(MSG_INFO,
+ "MBO: Reject WPA2 association without PMF");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_MBO */
+
+#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
+ if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+ enum oci_verify_result res;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (get_sta_tx_parameters(sta->wpa_sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx);
+ if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
+ res == OCI_NOT_FOUND) {
+ /* Work around misbehaving STAs */
+ wpa_printf(MSG_INFO,
+ "FILS: Disable OCV with a STA that does not send OCI");
+ wpa_auth_set_ocv(sta->wpa_sm, 0);
+ } else if (res != OCI_SUCCESS) {
+ wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
+ ocv_errorstr);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+ MACSTR " frame=fils-reassoc-req error=%s",
+ MAC2STR(sta->addr), ocv_errorstr);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+#endif /* CONFIG_FILS && CONFIG_OCV */
+
+ ap_copy_sta_supp_op_classes(sta, elems->supp_op_classes,
+ elems->supp_op_classes_len);
+
+ if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
+ elems->rrm_enabled &&
+ elems->rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
+ os_memcpy(sta->rrm_enabled_capa, elems->rrm_enabled,
+ sizeof(sta->rrm_enabled_capa));
+
+ if (elems->power_capab) {
+ sta->min_tx_power = elems->power_capab[0];
+ sta->max_tx_power = elems->power_capab[1];
+ sta->power_capab = 1;
+ } else {
+ sta->power_capab = 0;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ies, size_t ies_len, int reassoc)
+{
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station sent an invalid association request");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
+ false);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ u16 status_code,
+ u8 *buf, size_t buflen)
+{
+ u8 *p = buf;
+
+ /* Capability Info */
+ WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
+ p += 2;
+
+ /* Status Code */
+ WPA_PUT_LE16(p, status_code);
+ p += 2;
+
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return p - buf;
+
+ /* AID is not included */
+ p = hostapd_eid_supp_rates(hapd, p);
+ p = hostapd_eid_ext_supp_rates(hapd, p);
+ p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+ p = hostapd_eid_ht_capabilities(hapd, p);
+ p = hostapd_eid_ht_operation(hapd, p);
+
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ p = hostapd_eid_vht_capabilities(hapd, p, 0);
+ p = hostapd_eid_vht_operation(hapd, p);
+ }
+
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_spatial_reuse(hapd, p);
+ p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+ p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_eht_operation(hapd, p);
+ }
+ }
+
+ p = hostapd_eid_ext_capab(hapd, p, false);
+ p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+ p = hostapd_eid_wmm(hapd, p);
+
+ if (hapd->conf->assocresp_elements &&
+ (size_t) (buf + buflen - p) >=
+ wpabuf_len(hapd->conf->assocresp_elements)) {
+ os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+ wpabuf_len(hapd->conf->assocresp_elements));
+ p += wpabuf_len(hapd->conf->assocresp_elements);
+ }
+
+ return p - buf;
+}
+
+
+static void ieee80211_ml_process_link(struct hostapd_data *hapd,
+ struct sta_info *origin_sta,
+ struct mld_link_info *link,
+ const u8 *ies, size_t ies_len,
+ bool reassoc)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *mlbuf = NULL;
+ struct sta_info *sta = NULL;
+ u16 status = WLAN_STATUS_SUCCESS;
+
+ wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
+ hapd->mld_link_id, MAC2STR(link->peer_addr));
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+
+ sta = ap_get_sta(hapd, origin_sta->addr);
+ if (sta) {
+ wpa_printf(MSG_INFO, "MLD: link: Station already exists");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sta = NULL;
+ goto out;
+ }
+
+ sta = ap_sta_add(hapd, origin_sta->addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto out;
+ }
+
+ mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf)
+ goto out;
+
+ if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
+ hapd->mld_link_id, true) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: link: Failed to parse association request Multi-Link element");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+
+ sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
+ goto out;
+ }
+
+ sta->mld_info.mld_sta = true;
+ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
+ os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
+
+ /*
+ * Get the AID from the station on which the association was performed,
+ * and mark it as used.
+ */
+ sta->aid = origin_sta->aid;
+ if (sta->aid == 0) {
+ wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+ hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+ sta->listen_interval = origin_sta->listen_interval;
+ update_ht_state(hapd, sta);
+
+ /* RSN Authenticator should always be the one on the original station */
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ sta->wpa_sm = NULL;
+
+ /*
+ * Do not initialize the EAPOL state machine.
+ * TODO: Maybe it is needed?
+ */
+ sta->eapol_sm = NULL;
+
+ wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
+ hapd->mld_link_id, sta->aid);
+
+ /*
+ * Get RSNE and RSNXE for the current BSS as they are required by the
+ * Authenticator.
+ */
+ link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+ link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
+
+ /* TODO: What other processing is required? */
+
+ if (add_associated_sta(hapd, sta, reassoc))
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+out:
+ wpabuf_free(mlbuf);
+ link->status = status;
+
+ wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
+ if (sta && status != WLAN_STATUS_SUCCESS)
+ ap_free_sta(hapd, sta);
+
+ link->resp_sta_profile_len =
+ ieee80211_ml_build_assoc_resp(hapd, link->status,
+ link->resp_sta_profile,
+ sizeof(link->resp_sta_profile));
+}
+
+
+bool hostapd_is_mld_ap(struct hostapd_data *hapd)
+{
+ if (!hapd->conf->mld_ap)
+ return false;
+
+ if (!hapd->iface || !hapd->iface->interfaces ||
+ hapd->iface->interfaces->count <= 1)
+ return false;
+
+ return true;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ies, size_t ies_len,
+ bool reassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+ unsigned int i, j;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+ /*
+ * This is not really needed, but make the interaction with the RSN
+ * Authenticator more consistent
+ */
+ sta->mld_info.links[hapd->mld_link_id].rsne =
+ hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+ sta->mld_info.links[hapd->mld_link_id].rsnxe =
+ hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ struct hostapd_iface *iface = NULL;
+ struct mld_link_info *link = &sta->mld_info.links[i];
+
+ if (!link->valid)
+ continue;
+
+ for (j = 0; j < hapd->iface->interfaces->count; j++) {
+ iface = hapd->iface->interfaces->iface[j];
+
+ if (hapd->iface == iface)
+ continue;
+
+ if (iface->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
+ i == iface->bss[0]->mld_link_id)
+ break;
+ }
+
+ if (!iface || j == hapd->iface->interfaces->count) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: No link match for link_id=%u", i);
+
+ link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ link->resp_sta_profile_len =
+ ieee80211_ml_build_assoc_resp(
+ hapd, link->status,
+ link->resp_sta_profile,
+ sizeof(link->resp_sta_profile));
+ } else {
+ ieee80211_ml_process_link(iface->bss[0], sta, link,
+ ies, ies_len, reassoc);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
+ u16 reason_code)
+{
+ int send_len;
+ struct ieee80211_mgmt reply;
+
+ os_memset(&reply, 0, sizeof(reply));
+ reply.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
+ os_memcpy(reply.da, addr, ETH_ALEN);
+ os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
+
+ send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
+ reply.u.deauth.reason_code = host_to_le16(reason_code);
+
+ if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0)
+ wpa_printf(MSG_INFO, "Failed to send deauth: %s",
+ strerror(errno));
+}
+
+
+static int add_associated_sta(struct hostapd_data *hapd,
+ struct sta_info *sta, int reassoc)
+{
+ struct ieee80211_ht_capabilities ht_cap;
+ struct ieee80211_vht_capabilities vht_cap;
+ struct ieee80211_he_capabilities he_cap;
+ struct ieee80211_eht_capabilities eht_cap;
+ int set = 1;
+ const u8 *mld_link_addr = NULL;
+ bool mld_link_sta = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ u8 mld_link_id = hapd->mld_link_id;
+
+ mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
+ mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+
+ if (hapd->mld_link_id != sta->mld_assoc_link_id)
+ set = 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ /*
+ * Remove the STA entry to ensure the STA PS state gets cleared and
+ * configuration gets updated. This is relevant for cases, such as
+ * FT-over-the-DS, where a station re-associates back to the same AP but
+ * skips the authentication flow, or if working with a driver that
+ * does not support full AP client state.
+ *
+ * Skip this if the STA has already completed FT reassociation and the
+ * TK has been configured since the TX/RX PN must not be reset to 0 for
+ * the same key.
+ *
+ * FT-over-the-DS has a special case where the STA entry (and as such,
+ * the TK) has not yet been configured to the driver depending on which
+ * driver interface is used. For that case, allow add-STA operation to
+ * be used (instead of set-STA). This is needed to allow mac80211-based
+ * drivers to accept the STA parameter configuration. Since this is
+ * after a new FT-over-DS exchange, a new TK has been derived, so key
+ * reinstallation is not a concern for this case.
+ *
+ * If the STA was associated and authorized earlier, but came for a new
+ * connection (!added_unassoc + !reassoc), remove the existing STA entry
+ * so that it can be re-added. This case is rarely seen when the AP could
+ * not receive the deauth/disassoc frame from the STA. And the STA comes
+ * back with new connection within a short period or before the inactive
+ * STA entry is removed from the list.
+ */
+ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+ MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
+ sta->ft_over_ds, reassoc,
+ !!(sta->flags & WLAN_STA_AUTHORIZED),
+ wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
+ wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
+
+ if (!mld_link_sta && !sta->added_unassoc &&
+ (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
+ (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+ !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
+ (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+ set = 0;
+
+ /* Do not allow the FT-over-DS exception to be used more than
+ * once per authentication exchange to guarantee a new TK is
+ * used here */
+ sta->ft_over_ds = 0;
+ }
+
+ if (sta->flags & WLAN_STA_HT)
+ hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+#ifdef CONFIG_IEEE80211AC
+ if (sta->flags & WLAN_STA_VHT)
+ hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ if (sta->flags & WLAN_STA_HE) {
+ hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
+ sta->he_capab_len);
+ }
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ if (sta->flags & WLAN_STA_EHT)
+ hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
+ sta->eht_capab_len);
+#endif /* CONFIG_IEEE80211BE */
+
+ /*
+ * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
+ * will be set when the ACK frame for the (Re)Association Response frame
+ * is processed (TX status driver event).
+ */
+ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+ sta->supported_rates, sta->supported_rates_len,
+ sta->listen_interval,
+ sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+ sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+ sta->flags & WLAN_STA_HE ? &he_cap : NULL,
+ sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+ sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
+ sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
+ sta->he_6ghz_capab,
+ sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
+ sta->vht_opmode, sta->p2p_ie ? 1 : 0,
+ set, mld_link_addr, mld_link_sta)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
+ "Could not %s STA to kernel driver",
+ set ? "set" : "add");
+
+ if (sta->added_unassoc) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+
+ return -1;
+ }
+
+ sta->added_unassoc = 0;
+
+ return 0;
+}
+
+
+static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 status_code, int reassoc,
+ const u8 *ies, size_t ies_len, int rssi,
+ int omit_rsnxe)
+{
+ int send_len;
+ u8 *buf;
+ size_t buflen;
+ struct ieee80211_mgmt *reply;
+ u8 *p;
+ u16 res = WLAN_STATUS_SUCCESS;
+ const u8 *sa = hapd->own_addr;
+
+ buflen = sizeof(struct ieee80211_mgmt) + 1024;
+#ifdef CONFIG_FILS
+ if (sta && sta->fils_hlp_resp)
+ buflen += wpabuf_len(sta->fils_hlp_resp);
+ if (sta)
+ buflen += 150;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ buflen += 150;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+ if (sta && sta->dpp_pfs)
+ buflen += 5 + sta->dpp_pfs->curve->prime_len;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+ buflen += 3 + sizeof(struct ieee80211_eht_operation);
+ if (hapd->iconf->punct_bitmap)
+ buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ buf = os_zalloc(buflen);
+ if (!buf) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ reply = (struct ieee80211_mgmt *) buf;
+ reply->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+ WLAN_FC_STYPE_ASSOC_RESP));
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * Once a non-AP MLD is added to the driver, the addressing should use
+ * MLD MAC address.
+ */
+ if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta)
+ sa = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ os_memcpy(reply->da, addr, ETH_ALEN);
+ os_memcpy(reply->sa, sa, ETH_ALEN);
+ os_memcpy(reply->bssid, sa, ETH_ALEN);
+
+ send_len = IEEE80211_HDRLEN;
+ send_len += sizeof(reply->u.assoc_resp);
+ reply->u.assoc_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+ reply->u.assoc_resp.status_code = host_to_le16(status_code);
+
+ reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
+ BIT(14) | BIT(15));
+ /* Supported rates */
+ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
+ /* Extended supported rates */
+ p = hostapd_eid_ext_supp_rates(hapd, p);
+
+ /* Radio measurement capabilities */
+ p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+
+#ifdef CONFIG_MBO
+ if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
+ rssi != 0) {
+ int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
+
+ p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
+ delta);
+ }
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta && status_code == WLAN_STATUS_SUCCESS) {
+ /* IEEE 802.11r: Mobility Domain Information, Fast BSS
+ * Transition Information, RSN, [RIC Response] */
+ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
+ buf + buflen - p,
+ sta->auth_alg, ies, ies_len,
+ omit_rsnxe);
+ if (!p) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to write AssocResp IEs");
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_FILS
+ if (sta && status_code == WLAN_STATUS_SUCCESS &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK))
+ p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
+ buf + buflen - p,
+ ies, ies_len);
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if (sta && status_code == WLAN_STATUS_SUCCESS &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
+ buf + buflen - p,
+ ies, ies_len);
+#endif /* CONFIG_OWE */
+
+ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+ p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+ p = hostapd_eid_ht_capabilities(hapd, p);
+ p = hostapd_eid_ht_operation(hapd, p);
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+ !is_6ghz_op_class(hapd->iconf->op_class)) {
+ u32 nsts = 0, sta_nsts;
+
+ if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
+ struct ieee80211_vht_capabilities *capa;
+
+ nsts = (hapd->iface->conf->vht_capab >>
+ VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+ capa = sta->vht_capabilities;
+ sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
+ VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+
+ if (nsts < sta_nsts)
+ nsts = 0;
+ else
+ nsts = sta_nsts;
+ }
+ p = hostapd_eid_vht_capabilities(hapd, p, nsts);
+ p = hostapd_eid_vht_operation(hapd, p);
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_cca(hapd, p);
+ p = hostapd_eid_spatial_reuse(hapd, p);
+ p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+ p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ p = hostapd_eid_ext_capab(hapd, p, false);
+ p = hostapd_eid_bss_max_idle_period(hapd, p);
+ if (sta && sta->qos_map_enabled)
+ p = hostapd_eid_qos_map_set(hapd, p);
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
+ wpabuf_len(hapd->iface->fst_ies));
+ p += wpabuf_len(hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->rsnxe_override_ft &&
+ buf + buflen - p >=
+ (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) &&
+ sta && sta->auth_alg == WLAN_AUTH_FT) {
+ wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override");
+ os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft),
+ wpabuf_len(hapd->conf->rsnxe_override_ft));
+ p += wpabuf_len(hapd->conf->rsnxe_override_ft);
+ goto rsnxe_done;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!omit_rsnxe)
+ p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
+#ifdef CONFIG_TESTING_OPTIONS
+rsnxe_done:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ if (hapd->conf->mld_ap)
+ p = hostapd_eid_eht_basic_ml(hapd, p, sta, false);
+ p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_eht_operation(hapd, p);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ !wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ /* OWE Diffie-Hellman Parameter element */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+ WPA_PUT_LE16(p, sta->owe_group);
+ p += 2;
+ os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+ p += wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
+ os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
+ wpabuf_len(sta->dpp_pfs->ie));
+ p += wpabuf_len(sta->dpp_pfs->ie);
+ }
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_IEEE80211AC
+ if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+ p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
+ if (sta && (sta->flags & WLAN_STA_WMM))
+ p = hostapd_eid_wmm(hapd, p);
+
+#ifdef CONFIG_WPS
+ if (sta &&
+ ((sta->flags & WLAN_STA_WPS) ||
+ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
+ struct wpabuf *wps = wps_build_assoc_resp_ie();
+ if (wps) {
+ os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+ p += wpabuf_len(wps);
+ wpabuf_free(wps);
+ }
+ }
+#endif /* CONFIG_WPS */
+
+ if (sta && (sta->flags & WLAN_STA_MULTI_AP))
+ p = hostapd_eid_multi_ap(hapd, p);
+
+#ifdef CONFIG_P2P
+ if (sta && sta->p2p_ie && hapd->p2p_group) {
+ struct wpabuf *p2p_resp_ie;
+ enum p2p_status_code status;
+ switch (status_code) {
+ case WLAN_STATUS_SUCCESS:
+ status = P2P_SC_SUCCESS;
+ break;
+ case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+ status = P2P_SC_FAIL_LIMIT_REACHED;
+ break;
+ default:
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ break;
+ }
+ p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
+ if (p2p_resp_ie) {
+ os_memcpy(p, wpabuf_head(p2p_resp_ie),
+ wpabuf_len(p2p_resp_ie));
+ p += wpabuf_len(p2p_resp_ie);
+ wpabuf_free(p2p_resp_ie);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE)
+ p = hostapd_eid_p2p_manage(hapd, p);
+#endif /* CONFIG_P2P_MANAGER */
+
+ p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+
+ if (hapd->conf->assocresp_elements &&
+ (size_t) (buf + buflen - p) >=
+ wpabuf_len(hapd->conf->assocresp_elements)) {
+ os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+ wpabuf_len(hapd->conf->assocresp_elements));
+ p += wpabuf_len(hapd->conf->assocresp_elements);
+ }
+
+ send_len += p - reply->u.assoc_resp.variable;
+
+#ifdef CONFIG_FILS
+ if (sta &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+ status_code == WLAN_STATUS_SUCCESS) {
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+ ParseFailed || !elems.fils_session) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+
+ /* FILS Session */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + FILS_SESSION_LEN; /* Length */
+ *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+ os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
+ send_len += 2 + 1 + FILS_SESSION_LEN;
+
+ send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
+ buflen, sta->fils_hlp_resp);
+ if (send_len < 0) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ }
+#endif /* CONFIG_FILS */
+
+ if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) {
+ wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+ strerror(errno));
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+done:
+ os_free(buf);
+ return res;
+}
+
+
+#ifdef CONFIG_OWE
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *status)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->own_ie_override) {
+ wpa_printf(MSG_DEBUG, "OWE: Using IE override");
+ *status = WLAN_STATUS_SUCCESS;
+ return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ *status = WLAN_STATUS_SUCCESS;
+ return owe_buf;
+ }
+
+ if (sta->owe_pmk && sta->external_dh_updated) {
+ wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
+ *status = WLAN_STATUS_SUCCESS;
+ return owe_buf;
+ }
+
+ *status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+ if (*status != WLAN_STATUS_SUCCESS)
+ return NULL;
+
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+
+ if (sta->owe_ecdh && owe_buf) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ *status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return owe_buf;
+ }
+
+ /* OWE Diffie-Hellman Parameter element */
+ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+ */
+ WPA_PUT_LE16(owe_buf, sta->owe_group);
+ owe_buf += 2;
+ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+ owe_buf += wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+
+ return owe_buf;
+}
+#endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ u16 reply_res;
+
+ wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
+ MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ if (!sta->fils_pending_assoc_req)
+ return;
+ reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
+ sta->fils_pending_assoc_is_reassoc,
+ sta->fils_pending_assoc_req,
+ sta->fils_pending_assoc_req_len, 0, 0);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+
+ /*
+ * Remove the station in case transmission of a success response fails.
+ * At this point the station was already added associated to the driver.
+ */
+ if (reply_res != WLAN_STATUS_SUCCESS)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
+
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = eloop_data;
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP response timeout - continue with association response for "
+ MACSTR, MAC2STR(sta->addr));
+ if (sta->fils_drv_assoc_finish)
+ hostapd_notify_assoc_fils_finish(hapd, sta);
+ else
+ fils_hlp_finish_assoc(hapd, sta);
+}
+
+#endif /* CONFIG_FILS */
+
+
+static void handle_assoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int reassoc, int rssi)
+{
+ u16 capab_info, listen_interval, seq_ctrl, fc;
+ int resp = WLAN_STATUS_SUCCESS;
+ u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ const u8 *pos;
+ int left, i, ubus_resp;
+ struct sta_info *sta;
+ u8 *tmp = NULL;
+#ifdef CONFIG_FILS
+ int delay_assoc = 0;
+#endif /* CONFIG_FILS */
+ int omit_rsnxe = 0;
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+ wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
+ reassoc, (unsigned long) len);
+ return;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (reassoc) {
+ if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_reassoc_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring reassoc request from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+ } else {
+ if (hapd->iconf->ignore_assoc_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_assoc_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring assoc request from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ fc = le_to_host16(mgmt->frame_control);
+ seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+ if (reassoc) {
+ capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
+ listen_interval = le_to_host16(
+ mgmt->u.reassoc_req.listen_interval);
+ wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
+ " capab_info=0x%02x listen_interval=%d current_ap="
+ MACSTR " seq_ctrl=0x%x%s",
+ MAC2STR(mgmt->sa), capab_info, listen_interval,
+ MAC2STR(mgmt->u.reassoc_req.current_ap),
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+ left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+ pos = mgmt->u.reassoc_req.variable;
+ } else {
+ capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+ listen_interval = le_to_host16(
+ mgmt->u.assoc_req.listen_interval);
+ wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
+ " capab_info=0x%02x listen_interval=%d "
+ "seq_ctrl=0x%x%s",
+ MAC2STR(mgmt->sa), capab_info, listen_interval,
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+ left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+ pos = mgmt->u.assoc_req.variable;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta && sta->auth_alg == WLAN_AUTH_FT &&
+ (sta->flags & WLAN_STA_AUTH) == 0) {
+ wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
+ "prior to authentication since it is using "
+ "over-the-DS FT", MAC2STR(mgmt->sa));
+
+ /*
+ * Mark station as authenticated, to avoid adding station
+ * entry in the driver as associated and not authenticated
+ */
+ sta->flags |= WLAN_STA_AUTH;
+ } else
+#endif /* CONFIG_IEEE80211R_AP */
+ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
+ HOSTAPD_MODE_IEEE80211AD) {
+ int acl_res;
+ struct radius_sta info;
+
+ acl_res = ieee802_11_allowed_address(hapd, mgmt->sa,
+ (const u8 *) mgmt,
+ len, &info);
+ if (acl_res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Association Request frame from "
+ MACSTR " due to ACL reject",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (acl_res == HOSTAPD_ACL_PENDING)
+ return;
+
+ /* DMG/IEEE 802.11ad does not use authentication.
+ * Allocate sta entry upon association. */
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Failed to add STA");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ acl_res = ieee802_11_set_radius_info(
+ hapd, sta, acl_res, &info);
+ if (acl_res) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Skip authentication for DMG/IEEE 802.11ad");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_OPEN;
+ } else {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to associate before authentication (aid=%d flags=0x%x)",
+ sta ? sta->aid : -1,
+ sta ? sta->flags : 0);
+ send_deauth(hapd, mgmt->sa,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+ return;
+ }
+ }
+
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+ sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+ WLAN_FC_STYPE_ASSOC_REQ)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated association frame seq_ctrl=0x%x",
+ seq_ctrl);
+ return;
+ }
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+ WLAN_FC_STYPE_ASSOC_REQ;
+
+ if (hapd->tkip_countermeasures) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (listen_interval > hapd->conf->max_listen_interval) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Too large Listen Interval (%d)",
+ listen_interval);
+ resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
+ goto fail;
+ }
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
+ rssi < hapd->iconf->rssi_reject_assoc_rssi &&
+ (sta->auth_rssi == 0 ||
+ sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
+ resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
+ goto fail;
+ }
+#endif /* CONFIG_MBO */
+
+ /*
+ * sta->capability is used in check_assoc_ies() for RRM enabled
+ * capability element.
+ */
+ sta->capability = capab_info;
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ int res;
+
+ /* The end of the payload is encrypted. Need to decrypt it
+ * before parsing. */
+
+ tmp = os_memdup(pos, left);
+ if (!tmp) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+ len, tmp, left);
+ if (res < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ pos = tmp;
+ left = res;
+ }
+#endif /* CONFIG_FILS */
+ struct hostapd_ubus_request req = {
+ .type = HOSTAPD_UBUS_ASSOC_REQ,
+ .mgmt_frame = mgmt,
+ .ssi_signal = rssi,
+ };
+
+ /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ * is used */
+ resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+ omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+
+ if (hostapd_get_aid(hapd, sta) < 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ sta->listen_interval = listen_interval;
+
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ sta->flags |= WLAN_STA_NONERP;
+ for (i = 0; i < sta->supported_rates_len; i++) {
+ if ((sta->supported_rates[i] & 0x7f) > 22) {
+ sta->flags &= ~WLAN_STA_NONERP;
+ break;
+ }
+ }
+ if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
+ sta->nonerp_set = 1;
+ hapd->iface->num_sta_non_erp++;
+ if (hapd->iface->num_sta_non_erp == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+
+ if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
+ !sta->no_short_slot_time_set) {
+ sta->no_short_slot_time_set = 1;
+ hapd->iface->num_sta_no_short_slot_time++;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
+ HOSTAPD_MODE_IEEE80211G &&
+ hapd->iface->num_sta_no_short_slot_time == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+
+ if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+ else
+ sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+ if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+ !sta->no_short_preamble_set) {
+ sta->no_short_preamble_set = 1;
+ hapd->iface->num_sta_no_short_preamble++;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_preamble == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+
+ update_ht_state(hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "association OK (aid %d)", sta->aid);
+ /* Station will be marked associated, after it acknowledges AssocResp
+ */
+ sta->flags |= WLAN_STA_ASSOC_REQ_OK;
+
+ if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
+ wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
+ "SA Query procedure", reassoc ? "re" : "");
+ /* TODO: Send a protected Disassociate frame to the STA using
+ * the old key and Reason Code "Previous Authentication no
+ * longer valid". Make sure this is only sent protected since
+ * unprotected frame would be received by the STA that is now
+ * trying to associate.
+ */
+ }
+
+ /* Make sure that the previously registered inactivity timer will not
+ * remove the STA immediately. */
+ sta->timeout_next = STA_NULLFUNC;
+
+#ifdef CONFIG_TAXONOMY
+ taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
+#endif /* CONFIG_TAXONOMY */
+
+ sta->pending_wds_enable = 0;
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ if (fils_process_hlp(hapd, sta, pos, left) > 0)
+ delay_assoc = 1;
+ }
+#endif /* CONFIG_FILS */
+
+ ubus_resp = hostapd_ubus_handle_event(hapd, &req);
+ if (ubus_resp) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
+ MAC2STR(mgmt->sa));
+ resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ fail:
+
+ /*
+ * In case of a successful response, add the station to the driver.
+ * Otherwise, the kernel may ignore Data frames before we process the
+ * ACK frame (TX status). In case of a failure, this station will be
+ * removed.
+ *
+ * Note that this is not compliant with the IEEE 802.11 standard that
+ * states that a non-AP station should transition into the
+ * authenticated/associated state only after the station acknowledges
+ * the (Re)Association Response frame. However, still do this as:
+ *
+ * 1. In case the station does not acknowledge the (Re)Association
+ * Response frame, it will be removed.
+ * 2. Data frames will be dropped in the kernel until the station is
+ * set into authorized state, and there are no significant known
+ * issues with processing other non-Data Class 3 frames during this
+ * window.
+ */
+ if (resp == WLAN_STATUS_SUCCESS)
+ hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
+
+ if (resp == WLAN_STATUS_SUCCESS && sta &&
+ add_associated_sta(hapd, sta, reassoc))
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+#ifdef CONFIG_FILS
+ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
+ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
+ sta->fils_pending_assoc_req) {
+ /* Do not reschedule fils_hlp_timeout in case the station
+ * retransmits (Re)Association Request frame while waiting for
+ * the previously started FILS HLP wait, so that the timeout can
+ * be determined from the first pending attempt. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ os_free(tmp);
+ return;
+ }
+ if (sta) {
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ }
+ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+ sta->fils_pending_assoc_req = tmp;
+ sta->fils_pending_assoc_req_len = left;
+ sta->fils_pending_assoc_is_reassoc = reassoc;
+ sta->fils_drv_assoc_finish = 0;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
+ fils_hlp_timeout, hapd, sta);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ if (resp >= 0)
+ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc,
+ pos, left, rssi, omit_rsnxe);
+ os_free(tmp);
+
+ /*
+ * Remove the station in case transmission of a success response fails
+ * (the STA was added associated to the driver) or if the station was
+ * previously added unassociated.
+ */
+ if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
+ resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+}
+
+
+static void hostapd_deauth_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt)
+{
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "deauthentication: STA=" MACSTR " reason_code=%d",
+ MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
+
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ mlme_deauthenticate_indication(
+ hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+static void hostapd_disassoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt)
+{
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "disassocation: STA=" MACSTR " reason_code=%d",
+ MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code));
+
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated");
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+ * authenticated. */
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(hapd, sta);
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+
+ if (sta->timeout_next == STA_NULLFUNC ||
+ sta->timeout_next == STA_DISASSOC) {
+ sta->timeout_next = STA_DEAUTH;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ }
+
+ mlme_disassociate_indication(
+ hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+
+ /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
+ * disassociation. */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ sta->flags &= ~WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ ap_free_sta(hapd, sta);
+ }
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static struct sta_info *
+hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ struct hostapd_data **assoc_hapd)
+{
+ struct hostapd_data *other_hapd = NULL;
+ struct sta_info *tmp_sta;
+
+ *assoc_hapd = hapd;
+
+ /* The station is the one on which the association was performed */
+ if (sta->mld_assoc_link_id == hapd->mld_link_id)
+ return sta;
+
+ other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
+ if (!other_hapd) {
+ wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
+ sta->mld_assoc_link_id);
+ return sta;
+ }
+
+ /*
+ * Iterate over the stations and find the one with the matching link ID
+ * and association ID.
+ */
+ for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
+ if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
+ tmp_sta->aid == sta->aid) {
+ *assoc_hapd = other_hapd;
+ return tmp_sta;
+ }
+ }
+
+ return sta;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+ unsigned int i, link_id;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return false;
+
+ /*
+ * Get the station on which the association was performed, as it holds
+ * the information about all the other links.
+ */
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+
+ if (!assoc_sta->mld_info.links[link_id].valid)
+ continue;
+
+ tmp_hapd =
+ assoc_hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ /*
+ * Remove the station on which the association
+ * was done only after all other link stations
+ * are removed. Since there is only a single
+ * station per struct hostapd_hapd with the
+ * same association link simply break out from
+ * the loop.
+ */
+ if (tmp_sta == assoc_sta)
+ break;
+
+ if (tmp_sta->mld_assoc_link_id !=
+ assoc_sta->mld_assoc_link_id ||
+ tmp_sta->aid != assoc_sta->aid)
+ continue;
+
+ if (!disassoc)
+ hostapd_deauth_sta(tmp_hapd, tmp_sta,
+ mgmt);
+ else
+ hostapd_disassoc_sta(tmp_hapd, tmp_sta,
+ mgmt);
+ break;
+ }
+ }
+ }
+
+ /* Remove the station on which the association was performed. */
+ if (!disassoc)
+ hostapd_deauth_sta(assoc_hapd, assoc_sta, mgmt);
+ else
+ hostapd_disassoc_sta(assoc_hapd, assoc_sta, mgmt);
+
+ return true;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "handle_disassoc - too short payload (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+ hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " trying to disassociate, but it is not associated",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, true))
+ return;
+
+ hostapd_disassoc_sta(hapd, sta, mgmt);
+}
+
+
+static void handle_deauth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "handle_deauth - too short payload (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ /* Clear the PTKSA cache entries for PASN */
+ ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
+
+ hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " trying to deauthenticate, but it is not authenticated",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, false))
+ return;
+
+ hostapd_deauth_sta(hapd, sta, mgmt);
+}
+
+
+static void handle_beacon(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ struct hostapd_frame_info *fi)
+{
+ struct ieee802_11_elems elems;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
+ wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ (void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
+ len - (IEEE80211_HDRLEN +
+ sizeof(mgmt->u.beacon)), &elems,
+ 0);
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+ ap_list_process_beacon(hapd, mgmt, &elems, fi);
+#else
+ ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+}
+
+
+static int robust_action_frame(u8 category)
+{
+ return category != WLAN_ACTION_PUBLIC &&
+ category != WLAN_ACTION_HT;
+}
+
+
+static int handle_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ unsigned int freq)
+{
+ struct sta_info *sta;
+ u8 *action __maybe_unused;
+
+ if (len < IEEE80211_HDRLEN + 2 + 1) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "handle_action - too short payload (len=%lu)",
+ (unsigned long) len);
+ return 0;
+ }
+
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " len %d freq %u",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+ "frame (category=%u) from unassociated STA " MACSTR,
+ mgmt->u.action.category, MAC2STR(mgmt->sa));
+ return 0;
+ }
+
+ if (sta && (sta->flags & WLAN_STA_MFP) &&
+ !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
+ robust_action_frame(mgmt->u.action.category)) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Dropped unprotected Robust Action frame from "
+ "an MFP STA");
+ return 0;
+ }
+
+ if (sta) {
+ u16 fc = le_to_host16(mgmt->frame_control);
+ u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+ sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated action frame seq_ctrl=0x%x",
+ seq_ctrl);
+ return 1;
+ }
+
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = WLAN_FC_STYPE_ACTION;
+ }
+
+ switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R_AP
+ case WLAN_ACTION_FT:
+ if (!sta ||
+ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+ len - IEEE80211_HDRLEN))
+ break;
+ return 1;
+#endif /* CONFIG_IEEE80211R_AP */
+ case WLAN_ACTION_WMM:
+ hostapd_wmm_action(hapd, mgmt, len);
+ return 1;
+ case WLAN_ACTION_SA_QUERY:
+ ieee802_11_sa_query_action(hapd, mgmt, len);
+ return 1;
+#ifdef CONFIG_WNM_AP
+ case WLAN_ACTION_WNM:
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+ return 1;
+#endif /* CONFIG_WNM_AP */
+#ifdef CONFIG_FST
+ case WLAN_ACTION_FST:
+ if (hapd->iface->fst)
+ fst_rx_action(hapd->iface->fst, mgmt, len);
+ else
+ wpa_printf(MSG_DEBUG,
+ "FST: Ignore FST Action frame - no FST attached");
+ return 1;
+#endif /* CONFIG_FST */
+ case WLAN_ACTION_PUBLIC:
+ case WLAN_ACTION_PROTECTED_DUAL:
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_20_40_BSS_COEX) {
+ hostapd_2040_coex_action(hapd, mgmt, len);
+ return 1;
+ }
+#ifdef CONFIG_DPP
+ if (len >= IEEE80211_HDRLEN + 6 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.oui;
+ end = ((const u8 *) mgmt) + len;
+ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+ freq);
+ return 1;
+ }
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_INITIAL_RESP ||
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_COMEBACK_RESP)) {
+ const u8 *pos, *end;
+
+ pos = &mgmt->u.action.u.public_action.action;
+ end = ((const u8 *) mgmt) + len;
+ if (gas_query_ap_rx(hapd->gas, mgmt->sa,
+ mgmt->u.action.category,
+ pos, end - pos, freq) == 0)
+ return 1;
+ }
+#endif /* CONFIG_DPP */
+ if (hapd->public_action_cb) {
+ hapd->public_action_cb(hapd->public_action_cb_ctx,
+ (u8 *) mgmt, len, freq);
+ }
+ if (hapd->public_action_cb2) {
+ hapd->public_action_cb2(hapd->public_action_cb2_ctx,
+ (u8 *) mgmt, len, freq);
+ }
+ if (hapd->public_action_cb || hapd->public_action_cb2)
+ return 1;
+ break;
+ case WLAN_ACTION_VENDOR_SPECIFIC:
+ if (hapd->vendor_action_cb) {
+ if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+ (u8 *) mgmt, len, freq) == 0)
+ return 1;
+ }
+ break;
+ case WLAN_ACTION_RADIO_MEASUREMENT:
+ hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
+ return 1;
+ }
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "handle_action - unknown action category %d or invalid "
+ "frame",
+ mgmt->u.action.category);
+ if (!is_multicast_ether_addr(mgmt->da) &&
+ !(mgmt->u.action.category & 0x80) &&
+ !is_multicast_ether_addr(mgmt->sa)) {
+ struct ieee80211_mgmt *resp;
+
+ /*
+ * IEEE 802.11-REVma/D9.0 - 7.3.1.11
+ * Return the Action frame to the source without change
+ * except that MSB of the Category set to 1.
+ */
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
+ "frame back to sender");
+ resp = os_memdup(mgmt, len);
+ if (resp == NULL)
+ return 0;
+ os_memcpy(resp->da, resp->sa, ETH_ALEN);
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ resp->u.action.category |= 0x80;
+
+ if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) {
+ wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
+ "Action frame");
+ }
+ os_free(resp);
+ }
+
+ return 1;
+}
+
+
+/**
+ * notify_mgmt_frame - Notify of Management frames on the control interface
+ * @hapd: hostapd BSS data structure (the BSS to which the Management frame was
+ * sent to)
+ * @buf: Management frame data (starting from the IEEE 802.11 header)
+ * @len: Length of frame data in octets
+ *
+ * Notify the control interface of any received Management frame.
+ */
+static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf,
+ size_t len)
+{
+
+ int hex_len = len * 2 + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO,
+ AP_MGMT_FRAME_RECEIVED "buf=%s", hex);
+ os_free(hex);
+ }
+}
+
+
+/**
+ * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
+ * @hapd: hostapd BSS data structure (the BSS to which the management frame was
+ * sent to)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @fi: meta data about received frame (signal level, etc.)
+ *
+ * Process all incoming IEEE 802.11 management frames. This will be called for
+ * each frame received from the kernel driver through wlan#ap interface. In
+ * addition, it can be called to re-inserted pending frames (e.g., when using
+ * external RADIUS server as an MAC ACL).
+ */
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi)
+{
+ struct ieee80211_mgmt *mgmt;
+ u16 fc, stype;
+ int ret = 0;
+ unsigned int freq;
+ int ssi_signal = fi ? fi->ssi_signal : 0;
+
+ if (len < 24)
+ return 0;
+
+ if (fi && fi->freq)
+ freq = fi->freq;
+ else
+ freq = hapd->iface->freq;
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (is_multicast_ether_addr(mgmt->sa) ||
+ is_zero_ether_addr(mgmt->sa) ||
+ os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ /* Do not process any frames with unexpected/invalid SA so that
+ * we do not add any state for unexpected STA addresses or end
+ * up sending out frames to unexpected destination. */
+ wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR
+ " in received frame - ignore this frame silently",
+ MAC2STR(mgmt->sa));
+ return 0;
+ }
+
+ if (stype == WLAN_FC_STYPE_BEACON) {
+ handle_beacon(hapd, mgmt, len, fi);
+ return 1;
+ }
+
+ if (!is_broadcast_ether_addr(mgmt->bssid) &&
+#ifdef CONFIG_P2P
+ /* Invitation responses can be sent with the peer MAC as BSSID */
+ !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ stype == WLAN_FC_STYPE_ACTION) &&
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+ !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_IEEE80211BE
+ !(hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+#endif /* CONFIG_IEEE80211BE */
+ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
+ MAC2STR(mgmt->bssid));
+ return 0;
+ }
+
+ if (hapd->iface->state != HAPD_IFACE_ENABLED) {
+ wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
+ return 1;
+ }
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+ handle_probe_req(hapd, mgmt, len, ssi_signal);
+ return 1;
+ }
+
+ if ((!is_broadcast_ether_addr(mgmt->da) ||
+ stype != WLAN_FC_STYPE_ACTION) &&
+#ifdef CONFIG_IEEE80211BE
+ !(hapd->conf->mld_ap &&
+ os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+#endif /* CONFIG_IEEE80211BE */
+ os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "MGMT: DA=" MACSTR " not our address",
+ MAC2STR(mgmt->da));
+ return 0;
+ }
+
+ if (hapd->iconf->track_sta_max_num)
+ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
+
+ if (hapd->conf->notify_mgmt_frames)
+ notify_mgmt_frame(hapd, buf, len);
+
+ switch (stype) {
+ case WLAN_FC_STYPE_AUTH:
+ wpa_printf(MSG_DEBUG, "mgmt::auth");
+ handle_auth(hapd, mgmt, len, ssi_signal, 0);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
+ handle_assoc(hapd, mgmt, len, 0, ssi_signal);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
+ handle_assoc(hapd, mgmt, len, 1, ssi_signal);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ wpa_printf(MSG_DEBUG, "mgmt::disassoc");
+ handle_disassoc(hapd, mgmt, len);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_DEAUTH:
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
+ handle_deauth(hapd, mgmt, len);
+ ret = 1;
+ break;
+ case WLAN_FC_STYPE_ACTION:
+ wpa_printf(MSG_DEBUG, "mgmt::action");
+ ret = handle_action(hapd, mgmt, len, freq);
+ break;
+ default:
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "unknown mgmt frame subtype %d", stype);
+ break;
+ }
+
+ return ret;
+}
+
+
+static void handle_auth_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ u16 auth_alg, auth_transaction, status_code;
+ struct sta_info *sta;
+ bool success_status;
+
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
+ " not found",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+ (unsigned long) len);
+ auth_alg = 0;
+ auth_transaction = 0;
+ status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ status_code = le_to_host16(mgmt->u.auth.status_code);
+
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "did not acknowledge authentication response");
+ goto fail;
+ }
+
+ if (status_code == WLAN_STATUS_SUCCESS &&
+ ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "authenticated");
+ sta->flags |= WLAN_STA_AUTH;
+ if (sta->added_unassoc)
+ hostapd_set_sta_flags(hapd, sta);
+ return;
+ }
+
+fail:
+ success_status = status_code == WLAN_STATUS_SUCCESS;
+#ifdef CONFIG_SAE
+ if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1)
+ success_status = sae_status_success(hapd, status_code);
+#endif /* CONFIG_SAE */
+ if (!success_status && sta->added_unassoc) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+}
+
+
+static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *ifname_wds)
+{
+#ifdef CONFIG_WEP
+ int i;
+ struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+ if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+ return;
+
+ for (i = 0; i < 4; i++) {
+ if (ssid->wep.key[i] &&
+ hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
+ 0, i == ssid->wep.idx, NULL, 0,
+ ssid->wep.key[i], ssid->wep.len[i],
+ i == ssid->wep.idx ?
+ KEY_FLAG_GROUP_RX_TX_DEFAULT :
+ KEY_FLAG_GROUP_RX_TX)) {
+ wpa_printf(MSG_WARNING,
+ "Could not set WEP keys for WDS interface; %s",
+ ifname_wds);
+ break;
+ }
+ }
+#endif /* CONFIG_WEP */
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void ieee80211_ml_link_sta_assoc_cb(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct mld_link_info *link,
+ bool ok)
+{
+ if (!ok) {
+ hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+
+ /* The STA is added only in case of SUCCESS */
+ if (link->status == WLAN_STATUS_SUCCESS)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+
+ return;
+ }
+
+ if (link->status != WLAN_STATUS_SUCCESS)
+ return;
+
+ sta->flags |= WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+ ap_sta_set_authorized(hapd, sta, 1);
+
+ hostapd_set_sta_flags(hapd, sta);
+
+ /*
+ * TODOs:
+ * - IEEE 802.1X port enablement is not needed as done on the station
+ * doing the connection.
+ * - Not handling accounting
+ * - Need to handle VLAN configuration
+ */
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
+ struct sta_info *sta, bool ok)
+{
+#ifdef CONFIG_IEEE80211BE
+ unsigned int i, link_id;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &sta->mld_info.links[link_id];
+
+ if (!link->valid)
+ continue;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct hostapd_data *tmp_hapd =
+ hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (tmp_hapd->conf->mld_ap ||
+ hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ tmp_sta->mld_assoc_link_id !=
+ sta->mld_assoc_link_id ||
+ tmp_sta->aid != sta->aid)
+ continue;
+
+ ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
+ tmp_sta, link,
+ ok);
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void handle_assoc_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int reassoc, int ok)
+{
+ u16 status;
+ struct sta_info *sta;
+ int new_assoc = 1;
+
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ hapd->mld_link_id != sta->mld_assoc_link_id) {
+ /* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
+ wpa_printf(MSG_DEBUG,
+ "%s: MLD: ignore on link station (%d != %d)",
+ __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+ return;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+ sizeof(mgmt->u.assoc_resp))) {
+ wpa_printf(MSG_INFO,
+ "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+ reassoc, (unsigned long) len);
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ return;
+ }
+
+ if (reassoc)
+ status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+ else
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+ /* The STA is added only in case of SUCCESS */
+ if (status == WLAN_STATUS_SUCCESS)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+
+ goto handle_ml;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto handle_ml;
+
+ /* Stop previous accounting session, if one is started, and allocate
+ * new session id for the new session. */
+ accounting_sta_stop(hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "associated (aid %d)",
+ sta->aid);
+
+ if (sta->flags & WLAN_STA_ASSOC)
+ new_assoc = 0;
+ sta->flags |= WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+ !hapd->conf->osen) ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK ||
+ sta->auth_alg == WLAN_AUTH_FT) {
+ /*
+ * Open, static WEP, FT protocol, or FILS; no separate
+ * authorization step.
+ */
+ ap_sta_set_authorized(hapd, sta, 1);
+ }
+
+ if (reassoc)
+ mlme_reassociate_indication(hapd, sta);
+ else
+ mlme_associate_indication(hapd, sta);
+
+ sta->sa_query_timed_out = 0;
+
+ if (sta->eapol_sm == NULL) {
+ /*
+ * This STA does not use RADIUS server for EAP authentication,
+ * so bind it to the selected VLAN interface now, since the
+ * interface selection is not going to change anymore.
+ */
+ if (ap_sta_bind_vlan(hapd, sta) < 0)
+ goto handle_ml;
+ } else if (sta->vlan_id) {
+ /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
+ if (ap_sta_bind_vlan(hapd, sta) < 0)
+ goto handle_ml;
+ }
+
+ hostapd_set_sta_flags(hapd, sta);
+
+ if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
+ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
+ MACSTR " based on pending request",
+ MAC2STR(sta->addr));
+ sta->pending_wds_enable = 0;
+ sta->flags |= WLAN_STA_WDS;
+ }
+
+ /* WPS not supported on backhaul BSS. Disable 4addr mode on fronthaul */
+ if ((sta->flags & WLAN_STA_WDS) ||
+ (sta->flags & WLAN_STA_MULTI_AP &&
+ !(hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+ !(sta->flags & WLAN_STA_WPS))) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+
+ wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
+ MACSTR " (aid %u)",
+ MAC2STR(sta->addr), sta->aid);
+ ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+ sta->aid, 1);
+ if (!ret)
+ hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+ }
+
+ if (sta->auth_alg == WLAN_AUTH_FT)
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+ else
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+ hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_FILS
+ if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+ fils_set_tk(sta->wpa_sm) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ if (sta->pending_eapol_rx) {
+ struct os_reltime now, age;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
+ if (age.sec == 0 && age.usec < 200000) {
+ wpa_printf(MSG_DEBUG,
+ "Process pending EAPOL frame that was received from " MACSTR " just before association notification",
+ MAC2STR(sta->addr));
+ ieee802_1x_receive(
+ hapd, mgmt->da,
+ wpabuf_head(sta->pending_eapol_rx->buf),
+ wpabuf_len(sta->pending_eapol_rx->buf),
+ sta->pending_eapol_rx->encrypted);
+ }
+ wpabuf_free(sta->pending_eapol_rx->buf);
+ os_free(sta->pending_eapol_rx);
+ sta->pending_eapol_rx = NULL;
+ }
+
+handle_ml:
+ hostapd_ml_handle_assoc_cb(hapd, sta, ok);
+}
+
+
+static void handle_deauth_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ if (is_multicast_ether_addr(mgmt->da))
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+ if (ok)
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
+ MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+ "deauth", MAC2STR(sta->addr));
+
+ ap_sta_deauth_cb(hapd, sta);
+}
+
+
+static void handle_disassoc_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ if (is_multicast_ether_addr(mgmt->da))
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+ if (ok)
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
+ MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+ "disassoc", MAC2STR(sta->addr));
+
+ ap_sta_disassoc_cb(hapd, sta);
+}
+
+
+static void handle_action_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ const struct rrm_measurement_report_element *report;
+
+#ifdef CONFIG_DPP
+ if (len >= IEEE80211_HDRLEN + 6 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = &mgmt->u.action.u.vs_public_action.variable[1];
+ end = ((const u8 *) mgmt) + len;
+ hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
+ return;
+ }
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_INITIAL_REQ ||
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_COMEBACK_REQ)) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.public_action.variable;
+ end = ((const u8 *) mgmt) + len;
+ gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
+ return;
+ }
+#endif /* CONFIG_DPP */
+ if (is_multicast_ether_addr(mgmt->da))
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+
+#ifdef CONFIG_HS20
+ if (ok && len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.category == WLAN_ACTION_WNM &&
+ mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
+ sta->hs20_deauth_on_ack) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
+ " on acknowledging the WNM-Notification",
+ MAC2STR(sta->addr));
+ ap_sta_session_timeout(hapd, sta, 0);
+ return;
+ }
+#endif /* CONFIG_HS20 */
+
+ if (len < 24 + 5 + sizeof(*report))
+ return;
+ report = (const struct rrm_measurement_report_element *)
+ &mgmt->u.action.u.rrm.variable[2];
+ if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
+ mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
+ report->eid == WLAN_EID_MEASURE_REQUEST &&
+ report->len >= 3 &&
+ report->type == MEASURE_TYPE_BEACON)
+ hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+}
+
+
+/**
+ * ieee802_11_mgmt_cb - Process management frame TX status callback
+ * @hapd: hostapd BSS data structure (the BSS from which the management frame
+ * was sent from)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @stype: management frame subtype from frame control field
+ * @ok: Whether the frame was ACK'ed
+ */
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ u16 stype, int ok)
+{
+ const struct ieee80211_mgmt *mgmt;
+ mgmt = (const struct ieee80211_mgmt *) buf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_mgmt_frame_handling) {
+ size_t hex_len = 2 * len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "MGMT-TX-STATUS stype=%u ok=%d buf=%s",
+ stype, ok, hex);
+ os_free(hex);
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ switch (stype) {
+ case WLAN_FC_STYPE_AUTH:
+ wpa_printf(MSG_DEBUG, "mgmt::auth cb");
+ handle_auth_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_ASSOC_RESP:
+ wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
+ handle_assoc_cb(hapd, mgmt, len, 0, ok);
+ break;
+ case WLAN_FC_STYPE_REASSOC_RESP:
+ wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
+ handle_assoc_cb(hapd, mgmt, len, 1, ok);
+ break;
+ case WLAN_FC_STYPE_PROBE_RESP:
+ wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
+ break;
+ case WLAN_FC_STYPE_DEAUTH:
+ wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+ handle_deauth_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
+ handle_disassoc_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_ACTION:
+ wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
+ handle_action_cb(hapd, mgmt, len, ok);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
+ break;
+ }
+}
+
+
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
+ return;
+ if (sta->flags & WLAN_STA_PENDING_POLL) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
+ "activity poll", MAC2STR(sta->addr),
+ ack ? "ACKed" : "did not ACK");
+ if (ack)
+ sta->flags &= ~WLAN_STA_PENDING_POLL;
+ }
+
+ ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+}
+
+
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, dst);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+ MACSTR " that is not currently associated",
+ MAC2STR(dst));
+ return;
+ }
+
+ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+}
+
+
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
+ MAC2STR(sta->addr));
+ if (!(sta->flags & WLAN_STA_PENDING_POLL))
+ return;
+
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
+ "activity poll", MAC2STR(sta->addr));
+ sta->flags &= ~WLAN_STA_PENDING_POLL;
+}
+
+
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, src);
+ if (sta &&
+ ((sta->flags & WLAN_STA_ASSOC) ||
+ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+ if (!hapd->conf->wds_sta)
+ return;
+
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
+ WLAN_STA_ASSOC_REQ_OK) {
+ wpa_printf(MSG_DEBUG,
+ "Postpone 4-address WDS mode enabling for STA "
+ MACSTR " since TX status for AssocResp is not yet known",
+ MAC2STR(sta->addr));
+ sta->pending_wds_enable = 1;
+ return;
+ }
+
+ if (wds && !(sta->flags & WLAN_STA_WDS)) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+
+ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+ "STA " MACSTR " (aid %u)",
+ MAC2STR(sta->addr), sta->aid);
+ sta->flags |= WLAN_STA_WDS;
+ ret = hostapd_set_wds_sta(hapd, ifname_wds,
+ sta->addr, sta->aid, 1);
+ if (!ret)
+ hostapd_set_wds_encryption(hapd, sta,
+ ifname_wds);
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+ MACSTR, MAC2STR(src));
+ if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
+ os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
+ /* Broadcast bit set in SA or unexpected SA?! Ignore the frame
+ * silently. */
+ return;
+ }
+
+ if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
+ wpa_printf(MSG_DEBUG, "Association Response to the STA has "
+ "already been sent, but no TX status yet known - "
+ "ignore Class 3 frame issue with " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+
+ if (sta && (sta->flags & WLAN_STA_AUTH))
+ hostapd_drv_sta_disassoc(
+ hapd, src,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ else
+ hostapd_drv_sta_deauth(
+ hapd, src,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+}
+
+
+static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count,
+ enum max_tx_pwr_interpretation tx_pwr_intrpn,
+ u8 tx_pwr_cat, u8 tx_pwr)
+{
+ int i;
+
+ *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */
+ *eid++ = 2 + tx_pwr_count; /* Length */
+
+ /*
+ * Transmit Power Information field
+ * bits 0-2 : Maximum Transmit Power Count
+ * bits 3-5 : Maximum Transmit Power Interpretation
+ * bits 6-7 : Maximum Transmit Power Category
+ */
+ *eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6);
+
+ /* Maximum Transmit Power field */
+ for (i = 0; i <= tx_pwr_count; i++)
+ *eid++ = tx_pwr;
+
+ return eid;
+}
+
+
+/*
+ * TODO: Extract power limits from channel data after 6G regulatory
+ * support.
+ */
+#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT (-1) /* dBm/MHz */
+#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT 5 /* dBm/MHz */
+
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_config *iconf = iface->conf;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ struct hostapd_channel_data *chan;
+ int dfs, i;
+ u8 channel, tx_pwr_count, local_pwr_constraint;
+ int max_tx_power;
+ u8 tx_pwr;
+
+ if (!mode)
+ return eid;
+
+ if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
+ return eid;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].freq == iface->freq)
+ break;
+ }
+ if (i == mode->num_channels)
+ return eid;
+
+#ifdef CONFIG_IEEE80211AX
+ /* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United
+ * States): An AP that is an Indoor Access Point per regulatory rules
+ * shall send at least two Transmit Power Envelope elements in Beacon
+ * and Probe Response frames as follows:
+ * - Maximum Transmit Power Category subfield = Default;
+ * Unit interpretation = Regulatory client EIRP PSD
+ * - Maximum Transmit Power Category subfield = Subordinate Device;
+ * Unit interpretation = Regulatory client EIRP PSD
+ */
+ if (is_6ghz_op_class(iconf->op_class)) {
+ enum max_tx_pwr_interpretation tx_pwr_intrpn;
+
+ /* Same Maximum Transmit Power for all 20 MHz bands */
+ tx_pwr_count = 0;
+ tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+
+ /* Default Transmit Power Envelope for Global Operating Class */
+ tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+ eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+ REG_DEFAULT_CLIENT, tx_pwr);
+
+ /* Indoor Access Point must include an additional TPE for
+ * subordinate devices */
+ if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) {
+ /* TODO: Extract PSD limits from channel data */
+ tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
+ eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+ tx_pwr_intrpn,
+ REG_SUBORDINATE_CLIENT,
+ tx_pwr);
+ }
+
+ return eid;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ switch (hostapd_get_oper_chwidth(iconf)) {
+ case CONF_OPER_CHWIDTH_USE_HT:
+ if (iconf->secondary_channel == 0) {
+ /* Max Transmit Power count = 0 (20 MHz) */
+ tx_pwr_count = 0;
+ } else {
+ /* Max Transmit Power count = 1 (20, 40 MHz) */
+ tx_pwr_count = 1;
+ }
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
+ tx_pwr_count = 2;
+ break;
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ case CONF_OPER_CHWIDTH_160MHZ:
+ /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
+ tx_pwr_count = 3;
+ break;
+ default:
+ return eid;
+ }
+
+ /*
+ * Below local_pwr_constraint logic is referred from
+ * hostapd_eid_pwr_constraint.
+ *
+ * Check if DFS is required by regulatory.
+ */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0)
+ dfs = 0;
+
+ /*
+ * In order to meet regulations when TPC is not implemented using
+ * a transmit power that is below the legal maximum (including any
+ * mitigation factor) should help. In this case, indicate 3 dB below
+ * maximum allowed transmit power.
+ */
+ if (hapd->iconf->local_pwr_constraint == -1)
+ local_pwr_constraint = (dfs == 0) ? 0 : 3;
+ else
+ local_pwr_constraint = hapd->iconf->local_pwr_constraint;
+
+ /*
+ * A STA that is not an AP shall use a transmit power less than or
+ * equal to the local maximum transmit power level for the channel.
+ * The local maximum transmit power can be calculated from the formula:
+ * local max TX pwr = max TX pwr - local pwr constraint
+ * Where max TX pwr is maximum transmit power level specified for
+ * channel in Country element and local pwr constraint is specified
+ * for channel in this Power Constraint element.
+ */
+ chan = &mode->channels[i];
+ max_tx_power = chan->max_tx_power - local_pwr_constraint;
+
+ /*
+ * Local Maximum Transmit power is encoded as two's complement
+ * with a 0.5 dB step.
+ */
+ max_tx_power *= 2; /* in 0.5 dB steps */
+ if (max_tx_power > 127) {
+ /* 63.5 has special meaning of 63.5 dBm or higher */
+ max_tx_power = 127;
+ }
+ if (max_tx_power < -128)
+ max_tx_power = -128;
+ if (max_tx_power < 0)
+ tx_pwr = 0x80 + max_tx_power + 128;
+ else
+ tx_pwr = max_tx_power;
+
+ return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP,
+ 0 /* Reserved for bands other than 6 GHz */,
+ tx_pwr);
+}
+
+
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 bw, chan1, chan2 = 0;
+ int freq1;
+
+ if (!hapd->cs_freq_params.channel ||
+ (!hapd->cs_freq_params.vht_enabled &&
+ !hapd->cs_freq_params.he_enabled &&
+ !hapd->cs_freq_params.eht_enabled))
+ return eid;
+
+ /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80, 4: 320 */
+ switch (hapd->cs_freq_params.bandwidth) {
+ case 40:
+ bw = 0;
+ break;
+ case 80:
+ /* check if it's 80+80 */
+ if (!hapd->cs_freq_params.center_freq2)
+ bw = 1;
+ else
+ bw = 3;
+ break;
+ case 160:
+ bw = 2;
+ break;
+ case 320:
+ bw = 4;
+ break;
+ default:
+ /* not valid VHT bandwidth or not in CSA */
+ return eid;
+ }
+
+ freq1 = hapd->cs_freq_params.center_freq1 ?
+ hapd->cs_freq_params.center_freq1 :
+ hapd->cs_freq_params.freq;
+ if (ieee80211_freq_to_chan(freq1, &chan1) !=
+ HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ if (hapd->cs_freq_params.center_freq2 &&
+ ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+ &chan2) != HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
+ *eid++ = 5; /* Length of Channel Switch Wrapper */
+ *eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+ *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+ *eid++ = bw; /* New Channel Width */
+ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+ return eid;
+}
+
+
+static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd,
+ size_t *current_len)
+{
+ struct hostapd_neighbor_entry *nr;
+ size_t total_len = 0, len = *current_len;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (!nr->nr || wpabuf_len(nr->nr) < 12)
+ continue;
+
+ if (nr->short_ssid == hapd->conf->ssid.short_ssid)
+ continue;
+
+ /* Start a new element */
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ len = RNR_HEADER_LEN;
+ total_len += RNR_HEADER_LEN;
+ }
+
+ len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
+ total_len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
+ }
+
+ *current_len = len;
+ return total_len;
+}
+
+
+struct mbssid_ie_profiles {
+ u8 start;
+ u8 end;
+};
+
+static size_t
+hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ size_t *current_len,
+ struct mbssid_ie_profiles *skip_profiles)
+{
+ size_t total_len = 0, len = *current_len;
+ int tbtt_count = 0;
+ size_t i, start = 0;
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ while (start < hapd->iface->num_bss) {
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ len = RNR_HEADER_LEN;
+ total_len += RNR_HEADER_LEN;
+ }
+
+ len += RNR_TBTT_HEADER_LEN;
+ total_len += RNR_TBTT_HEADER_LEN;
+
+ for (i = start; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+
+ if (!bss || !bss->conf || !bss->started)
+ continue;
+
+ if (bss == reporting_hapd ||
+ bss->conf->ignore_broadcast_ssid)
+ continue;
+
+ if (skip_profiles &&
+ i >= skip_profiles->start && i < skip_profiles->end)
+ continue;
+
+ if (len + RNR_TBTT_INFO_LEN > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ break;
+
+ if (!ap_mld) {
+ len += RNR_TBTT_INFO_LEN;
+ total_len += RNR_TBTT_INFO_LEN;
+ } else {
+ len += RNR_TBTT_INFO_MLD_LEN;
+ total_len += RNR_TBTT_INFO_MLD_LEN;
+ }
+ tbtt_count++;
+ }
+ start = i;
+ }
+
+ if (!tbtt_count)
+ total_len = 0;
+ else
+ *current_len = len;
+
+ return total_len;
+}
+
+
+enum colocation_mode {
+ NO_COLOCATED_6GHZ,
+ STANDALONE_6GHZ,
+ COLOCATED_6GHZ,
+ COLOCATED_LOWER_BAND,
+};
+
+static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
+{
+ u8 i;
+ bool is_6ghz = is_6ghz_op_class(hapd->iconf->op_class);
+
+ if (!hapd->iface || !hapd->iface->interfaces)
+ return NO_COLOCATED_6GHZ;
+
+ if (is_6ghz && hapd->iface->interfaces->count == 1)
+ return STANDALONE_6GHZ;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *iface;
+ bool is_colocated_6ghz;
+
+ iface = hapd->iface->interfaces->iface[i];
+ if (iface == hapd->iface || !iface || !iface->conf)
+ continue;
+
+ is_colocated_6ghz = is_6ghz_op_class(iface->conf->op_class);
+ if (!is_6ghz && is_colocated_6ghz)
+ return COLOCATED_LOWER_BAND;
+ if (is_6ghz && !is_colocated_6ghz)
+ return COLOCATED_6GHZ;
+ }
+
+ if (is_6ghz)
+ return STANDALONE_6GHZ;
+
+ return NO_COLOCATED_6GHZ;
+}
+
+
+static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+ size_t *current_len)
+{
+ struct hostapd_iface *iface;
+ size_t len = 0;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces)
+ return 0;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+ ap_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (iface == hapd->iface ||
+ !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+ continue;
+
+ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+ current_len, NULL);
+ }
+
+ return len;
+}
+
+
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+{
+ size_t total_len = 0, current_len = 0;
+ enum colocation_mode mode = get_colocation_mode(hapd);
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+ if (hapd->conf->rnr)
+ total_len += hostapd_eid_nr_db_len(hapd, ¤t_len);
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+ if (mode == COLOCATED_LOWER_BAND || ap_mld)
+ total_len +=
+ hostapd_eid_rnr_multi_iface_len(hapd,
+ ¤t_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+ !hapd->iconf->mbssid)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ ¤t_len,
+ NULL);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ ¤t_len,
+ NULL);
+ break;
+
+ default:
+ break;
+ }
+
+ return total_len;
+}
+
+
+static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
+{
+ struct hostapd_neighbor_entry *nr;
+ size_t len = *current_len;
+ u8 *size_offset = (eid - len) + 1;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (!nr->nr || wpabuf_len(nr->nr) < 12)
+ continue;
+
+ if (nr->short_ssid == hapd->conf->ssid.short_ssid)
+ continue;
+
+ /* Start a new element */
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+ size_offset = eid++;
+ len = RNR_HEADER_LEN;
+ }
+
+ /* TBTT Information Header subfield (2 octets) */
+ *eid++ = 0;
+ /* TBTT Information Length */
+ *eid++ = RNR_TBTT_INFO_LEN;
+ /* Operating Class */
+ *eid++ = wpabuf_head_u8(nr->nr)[10];
+ /* Channel Number */
+ *eid++ = wpabuf_head_u8(nr->nr)[11];
+ len += RNR_TBTT_HEADER_LEN;
+ /* TBTT Information Set */
+ /* TBTT Information field */
+ /* Neighbor AP TBTT Offset */
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ /* BSSID */
+ os_memcpy(eid, nr->bssid, ETH_ALEN);
+ eid += ETH_ALEN;
+ /* Short SSID */
+ os_memcpy(eid, &nr->short_ssid, 4);
+ eid += 4;
+ /* BSS parameters */
+ *eid++ = nr->bss_parameters;
+ /* 20 MHz PSD */
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+ len += RNR_TBTT_INFO_LEN;
+ *size_offset = (eid - size_offset) - 1;
+ }
+
+ *current_len = len;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ u8 *eid, size_t *current_len,
+ struct mbssid_ie_profiles *skip_profiles)
+{
+ struct hostapd_data *bss;
+ struct hostapd_iface *iface = hapd->iface;
+ size_t i, start = 0;
+ size_t len = *current_len;
+ u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
+ u8 tbtt_count = 0, op_class, channel, bss_param;
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
+ return eid;
+
+ if (ieee80211_freq_to_channel_ext(iface->freq,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return eid;
+
+ while (start < iface->num_bss) {
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ eid_start = eid;
+ *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+ size_offset = eid++;
+ len = RNR_HEADER_LEN;
+ tbtt_count = 0;
+ }
+
+ tbtt_count_pos = eid++;
+ *eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+ *eid++ = op_class;
+ *eid++ = hapd->iconf->channel;
+ len += RNR_TBTT_HEADER_LEN;
+
+ for (i = start; i < iface->num_bss; i++) {
+ bss_param = 0;
+ bss = iface->bss[i];
+ if (!bss || !bss->conf || !bss->started)
+ continue;
+
+ if (bss == reporting_hapd ||
+ bss->conf->ignore_broadcast_ssid)
+ continue;
+
+ if (skip_profiles &&
+ i >= skip_profiles->start && i < skip_profiles->end)
+ continue;
+
+ if (len + RNR_TBTT_INFO_LEN > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ break;
+
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
+ eid += ETH_ALEN;
+ os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
+ eid += 4;
+ if (bss->conf->ssid.short_ssid ==
+ reporting_hapd->conf->ssid.short_ssid)
+ bss_param |= RNR_BSS_PARAM_SAME_SSID;
+
+ if (iface->conf->mbssid != MBSSID_DISABLED &&
+ iface->num_bss > 1) {
+ bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+ if (bss == hostapd_mbssid_get_tx_bss(hapd))
+ bss_param |=
+ RNR_BSS_PARAM_TRANSMITTED_BSSID;
+ }
+
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ bss->conf->unsol_bcast_probe_resp_interval)
+ bss_param |=
+ RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+
+ bss_param |= RNR_BSS_PARAM_CO_LOCATED;
+
+ *eid++ = bss_param;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+
+ if (!ap_mld) {
+ len += RNR_TBTT_INFO_LEN;
+ } else {
+#ifdef CONFIG_IEEE80211BE
+ *eid++ = hapd->conf->mld_id;
+ *eid++ = hapd->mld_link_id | (1 << 4);
+ *eid++ = 0;
+ len += RNR_TBTT_INFO_MLD_LEN;
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ tbtt_count += 1;
+ }
+
+ start = i;
+ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+ *size_offset = (eid - size_offset) - 1;
+ }
+
+ if (tbtt_count == 0)
+ return eid_start;
+
+ *current_len = len;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
+{
+ struct hostapd_iface *iface;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces)
+ return eid;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+ hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+ ap_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (iface == hapd->iface ||
+ !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+ continue;
+
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ current_len, NULL);
+ }
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+{
+ u8 *eid_start = eid;
+ size_t current_len = 0;
+ enum colocation_mode mode = get_colocation_mode(hapd);
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+ if (hapd->conf->rnr)
+ eid = hostapd_eid_nr_db(hapd, eid, ¤t_len);
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+ if (mode == COLOCATED_LOWER_BAND || ap_mld)
+ eid = hostapd_eid_rnr_multi_iface(hapd, eid,
+ ¤t_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+ !hapd->iconf->mbssid)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ ¤t_len, NULL);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ ¤t_len, NULL);
+ break;
+
+ default:
+ return eid_start;
+ }
+
+ if (eid == eid_start + 2)
+ return eid_start;
+
+ return eid;
+}
+
+
+static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
+ size_t known_bss_len)
+{
+ if (!known_bss || known_bss_len <= i / 8)
+ return false;
+ known_bss = &known_bss[i / 8];
+ return *known_bss & (u8) (BIT(i % 8));
+}
+
+
+static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+ u32 frame_type, size_t *bss_index,
+ const u8 *known_bss,
+ size_t known_bss_len)
+{
+ struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ size_t len = 3, i;
+
+ for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+ const u8 *auth, *rsn = NULL, *rsnx = NULL;
+ size_t nontx_profile_len, auth_len;
+ u8 ie_count = 0;
+
+ if (!bss || !bss->conf || !bss->started ||
+ mbssid_known_bss(i, known_bss, known_bss_len))
+ continue;
+
+ /*
+ * Sublement ID: 1 octet
+ * Length: 1 octet
+ * Nontransmitted capabilities: 4 octets
+ * SSID element: 2 + variable
+ * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
+ * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
+ */
+ nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
+
+ if (frame_type == WLAN_FC_STYPE_BEACON)
+ nontx_profile_len += 2;
+
+ auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+ if (auth) {
+ rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+ if (rsn)
+ nontx_profile_len += 2 + rsn[1];
+
+ rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+ if (rsnx)
+ nontx_profile_len += 2 + rsnx[1];
+ }
+ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+ ie_count++;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ ie_count++;
+ if (bss->conf->xrates_supported)
+ nontx_profile_len += 8;
+ else if (hapd->conf->xrates_supported)
+ ie_count++;
+ if (ie_count)
+ nontx_profile_len += 4 + ie_count;
+
+ if (len + nontx_profile_len > 255)
+ break;
+
+ len += nontx_profile_len;
+ }
+
+ *bss_index = i;
+ return len;
+}
+
+
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ u8 *elem_count, const u8 *known_bss,
+ size_t known_bss_len, size_t *rnr_len)
+{
+ size_t len = 0, bss_index = 1;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_type != WLAN_FC_STYPE_BEACON &&
+ frame_type != WLAN_FC_STYPE_PROBE_RESP))
+ return 0;
+
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ if (!elem_count) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return 0;
+ }
+ *elem_count = 0;
+ }
+
+ while (bss_index < hapd->iface->num_bss) {
+ size_t rnr_count = bss_index;
+
+ len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+ &bss_index, known_bss,
+ known_bss_len);
+
+ if (frame_type == WLAN_FC_STYPE_BEACON)
+ *elem_count += 1;
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len) {
+ size_t rnr_cur_len = 0;
+ struct mbssid_ie_profiles skip_profiles = {
+ rnr_count, bss_index
+ };
+
+ *rnr_len += hostapd_eid_rnr_iface_len(
+ hapd, hostapd_mbssid_get_tx_bss(hapd),
+ &rnr_cur_len, &skip_profiles);
+ }
+ }
+
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
+ *rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
+
+ return len;
+}
+
+
+static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ u32 frame_type, u8 max_bssid_indicator,
+ size_t *bss_index, u8 elem_count,
+ const u8 *known_bss, size_t known_bss_len)
+{
+ struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ size_t i;
+ u8 *eid_len_offset, *max_bssid_indicator_offset;
+
+ *eid++ = WLAN_EID_MULTIPLE_BSSID;
+ eid_len_offset = eid++;
+ max_bssid_indicator_offset = eid++;
+
+ for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+ struct hostapd_bss_config *conf;
+ u8 *eid_len_pos, *nontx_bss_start = eid;
+ const u8 *auth, *rsn = NULL, *rsnx = NULL;
+ u8 ie_count = 0, non_inherit_ie[3];
+ size_t auth_len = 0;
+ u16 capab_info;
+
+ if (!bss || !bss->conf || !bss->started ||
+ mbssid_known_bss(i, known_bss, known_bss_len))
+ continue;
+ conf = bss->conf;
+
+ *eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
+ eid_len_pos = eid++;
+
+ capab_info = hostapd_own_capab_info(bss);
+ *eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
+ *eid++ = sizeof(capab_info);
+ WPA_PUT_LE16(eid, capab_info);
+ eid += sizeof(capab_info);
+
+ *eid++ = WLAN_EID_SSID;
+ *eid++ = conf->ssid.ssid_len;
+ os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
+ eid += conf->ssid.ssid_len;
+
+ *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
+ if (frame_type == WLAN_FC_STYPE_BEACON) {
+ *eid++ = 3;
+ *eid++ = i; /* BSSID Index */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ (conf->dtim_period % elem_count))
+ conf->dtim_period = elem_count;
+ *eid++ = conf->dtim_period;
+ *eid++ = 0xFF; /* DTIM Count */
+ } else {
+ /* Probe Request frame does not include DTIM Period and
+ * DTIM Count fields. */
+ *eid++ = 1;
+ *eid++ = i; /* BSSID Index */
+ }
+
+ auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+ if (auth) {
+ rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+ if (rsn) {
+ os_memcpy(eid, rsn, 2 + rsn[1]);
+ eid += 2 + rsn[1];
+ }
+
+ rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+ if (rsnx) {
+ os_memcpy(eid, rsnx, 2 + rsnx[1]);
+ eid += 2 + rsnx[1];
+ }
+ }
+ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSN;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+ if (hapd->conf->xrates_supported &&
+ !bss->conf->xrates_supported)
+ non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+ if (ie_count) {
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 2 + ie_count;
+ *eid++ = WLAN_EID_EXT_NON_INHERITANCE;
+ *eid++ = ie_count;
+ os_memcpy(eid, non_inherit_ie, ie_count);
+ eid += ie_count;
+ }
+
+ *eid_len_pos = (eid - eid_len_pos) - 1;
+
+ if (((eid - eid_len_offset) - 1) > 255) {
+ eid = nontx_bss_start;
+ break;
+ }
+ }
+
+ *bss_index = i;
+ *max_bssid_indicator_offset = max_bssid_indicator;
+ if (*max_bssid_indicator_offset < 1)
+ *max_bssid_indicator_offset = 1;
+ *eid_len_offset = (eid - eid_len_offset) - 1;
+ return eid;
+}
+
+
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ unsigned int frame_stype, u8 elem_count,
+ u8 **elem_offset,
+ const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
+ u8 *rnr_count, u8 **rnr_offset, size_t rnr_len)
+{
+ size_t bss_index = 1, cur_len = 0;
+ u8 elem_index = 0, *rnr_start_eid = rnr_eid;
+ bool add_rnr;
+
+ if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+ (frame_stype != WLAN_FC_STYPE_BEACON &&
+ frame_stype != WLAN_FC_STYPE_PROBE_RESP))
+ return eid;
+
+ if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
+ wpa_printf(MSG_INFO,
+ "MBSSID: Insufficient data for Beacon frames");
+ return eid;
+ }
+
+ add_rnr = hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+ frame_stype == WLAN_FC_STYPE_BEACON &&
+ rnr_eid && rnr_count && rnr_offset && rnr_len;
+
+ while (bss_index < hapd->iface->num_bss) {
+ unsigned int rnr_start_count = bss_index;
+
+ if (frame_stype == WLAN_FC_STYPE_BEACON) {
+ if (elem_index == elem_count) {
+ wpa_printf(MSG_WARNING,
+ "MBSSID: Larger number of elements than there is room in the provided array");
+ break;
+ }
+
+ elem_offset[elem_index] = eid;
+ elem_index = elem_index + 1;
+ }
+ eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+ hostapd_max_bssid_indicator(hapd),
+ &bss_index, elem_count,
+ known_bss, known_bss_len);
+
+ if (add_rnr) {
+ struct mbssid_ie_profiles skip_profiles = {
+ rnr_start_count, bss_index
+ };
+
+ rnr_offset[*rnr_count] = rnr_eid;
+ *rnr_count = *rnr_count + 1;
+ cur_len = 0;
+ rnr_eid = hostapd_eid_rnr_iface(
+ hapd, hostapd_mbssid_get_tx_bss(hapd),
+ rnr_eid, &cur_len, &skip_profiles);
+ }
+ }
+
+ if (add_rnr && (size_t) (rnr_eid - rnr_start_eid) < rnr_len) {
+ rnr_offset[*rnr_count] = rnr_eid;
+ *rnr_count = *rnr_count + 1;
+ cur_len = 0;
+
+ if (hapd->conf->rnr)
+ rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
+ if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
+ rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
+ &cur_len);
+ }
+
+ return eid;
+}
+
+
+static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
+{
+ u8 first_chan = *seg0 - 6, sec_chan;
+
+ switch (bitmap) {
+ case 0x6:
+ *seg0 = 0;
+ return;
+ case 0x8:
+ case 0x4:
+ case 0x2:
+ case 0x1:
+ case 0xC:
+ case 0x3:
+ if (pri_chan < *seg0)
+ *seg0 -= 4;
+ else
+ *seg0 += 4;
+ break;
+ }
+
+ if (pri_chan < *seg0)
+ sec_chan = pri_chan + 4;
+ else
+ sec_chan = pri_chan - 4;
+
+ if (bitmap & BIT((sec_chan - first_chan) / 4))
+ *seg0 = 0;
+}
+
+
+static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
+ enum oper_chan_width *width, u8 *seg0)
+{
+ if (pri < *seg0) {
+ *seg0 -= 8;
+ if (bitmap & 0x0F) {
+ *width = 0;
+ punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+ }
+ } else {
+ *seg0 += 8;
+ if (bitmap & 0xF0) {
+ *width = 0;
+ punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
+ seg0);
+ }
+ }
+}
+
+
+void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
+ u8 *seg0, u8 *seg1)
+{
+ if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
+ *width = CONF_OPER_CHWIDTH_USE_HT;
+ punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+ }
+
+ if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
+ *width = CONF_OPER_CHWIDTH_80MHZ;
+ *seg1 = 0;
+ punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
+ }
+
+ /* TODO: 320 MHz */
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.h
new file mode 100644
index 0000000..71e0074
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.h
@@ -0,0 +1,241 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct ieee80211_mgmt;
+struct radius_sta;
+enum ieee80211_op_mode;
+enum oper_chan_width;
+struct ieee802_11_elems;
+
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ u16 stype, int ok);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+#ifdef NEED_AP_MLME
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+#endif /* NEED_AP_MLME */
+u16 hostapd_own_capab_info(struct hostapd_data *hapd);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ bool mbssid_complete);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+ size_t len);
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
+
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+ struct ieee80211_ht_capabilities *ht_cap,
+ struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+ struct ieee80211_vht_capabilities *vht_cap,
+ struct ieee80211_vht_capabilities *neg_vht_cap);
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+ const struct ieee80211_he_capabilities *he_cap,
+ struct ieee80211_he_capabilities *neg_he_cap,
+ size_t he_capab_len);
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+ const struct ieee80211_eht_capabilities *src,
+ struct ieee80211_eht_capabilities *dest,
+ size_t len);
+u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
+ struct sta_info *info, bool include_mld_id);
+struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
+const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len);
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+ struct ieee802_11_elems *elems,
+ struct sta_info *sta);
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len);
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab);
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper);
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_opmode);
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode, const u8 *he_capab,
+ size_t he_capab_len);
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_6ghz_capab);
+int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
+ enum ieee80211_op_mode mode);
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds);
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid);
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len);
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
+int hostapd_update_time_adv(struct hostapd_data *hapd);
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_MBO
+
+u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
+
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+ size_t len, int delta);
+
+#else /* CONFIG_MBO */
+
+static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+ return eid;
+}
+
+static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MBO */
+
+void ap_copy_sta_supp_op_classes(struct sta_info *sta,
+ const u8 *supp_op_classes,
+ size_t supp_op_classes_len);
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len);
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *status);
+u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *rsn_ie, size_t rsn_ie_len,
+ const u8 *owe_dh, size_t owe_dh_len);
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+ const u8 *rsn_ie, size_t rsn_ie_len,
+ const u8 *owe_dh, size_t owe_dh_len);
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, size_t len, u16 auth_alg,
+ u16 auth_transaction, u16 status_code,
+ void (*cb)(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ u16 resp, struct wpabuf *data, int pub));
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len);
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, struct radius_sta *info);
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode,
+ const u8 *he_capab, size_t he_capab_len,
+ const u8 *eht_capab, size_t eht_capab_len);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+ u8 *elem_count, const u8 *known_bss,
+ size_t known_bss_len, size_t *rnr_len);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+ unsigned int frame_stype, u8 elem_count,
+ u8 **elem_offset,
+ const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
+ u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
+void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
+ enum oper_chan_width *width, u8 *seg0, u8 *seg1);
+bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+
+#endif /* IEEE802_11_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.c
new file mode 100644
index 0000000..4277d82
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.c
@@ -0,0 +1,745 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Access control list for IEEE 802.11 authentication can uses statically
+ * configured ACL from configuration files or an external RADIUS server.
+ * Results from external RADIUS queries are cached to allow faster
+ * authentication frame processing.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+
+#define RADIUS_ACL_TIMEOUT 30
+
+
+struct hostapd_cached_radius_acl {
+ struct os_reltime timestamp;
+ macaddr addr;
+ int accepted; /* HOSTAPD_ACL_* */
+ struct hostapd_cached_radius_acl *next;
+ struct radius_sta info;
+};
+
+
+struct hostapd_acl_query_data {
+ struct os_reltime timestamp;
+ u8 radius_id;
+ macaddr addr;
+ u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
+ size_t auth_msg_len;
+ struct hostapd_acl_query_data *next;
+ bool radius_psk;
+ int akm;
+ u8 *anonce;
+ u8 *eapol;
+ size_t eapol_len;
+};
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
+{
+ os_free(e->info.identity);
+ os_free(e->info.radius_cui);
+ hostapd_free_psk_list(e->info.psk);
+ os_free(e);
+}
+
+
+static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
+{
+ struct hostapd_cached_radius_acl *prev;
+
+ while (acl_cache) {
+ prev = acl_cache;
+ acl_cache = acl_cache->next;
+ hostapd_acl_cache_free_entry(prev);
+ }
+}
+
+
+static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
+ struct radius_sta *out)
+{
+ struct hostapd_cached_radius_acl *entry;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+
+ for (entry = hapd->acl_cache; entry; entry = entry->next) {
+ if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+ continue;
+
+ if (os_reltime_expired(&now, &entry->timestamp,
+ RADIUS_ACL_TIMEOUT))
+ return -1; /* entry has expired */
+ *out = entry->info;
+
+ return entry->accepted;
+ }
+
+ return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+ if (!query)
+ return;
+ os_free(query->auth_msg);
+ os_free(query->anonce);
+ os_free(query->eapol);
+ os_free(query);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
+ struct hostapd_acl_query_data *query)
+{
+ struct radius_msg *msg;
+ char buf[128];
+
+ query->radius_id = radius_client_get_id(hapd->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
+ if (!msg)
+ return -1;
+
+ if (radius_msg_make_authenticator(msg) < 0) {
+ wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+ os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add User-Name");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_user_password(
+ msg, (u8 *) buf, os_strlen(buf),
+ hapd->conf->radius->auth_server->shared_secret,
+ hapd->conf->radius->auth_server->shared_secret_len)) {
+ wpa_printf(MSG_DEBUG, "Could not add User-Password");
+ goto fail;
+ }
+
+ if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
+ NULL, msg) < 0)
+ goto fail;
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
+ goto fail;
+ }
+
+ if (query->akm &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+ wpa_akm_to_suite(query->akm))) {
+ wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite");
+ goto fail;
+ }
+
+ if (query->anonce &&
+ !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+ RADIUS_VENDOR_ID_FREERADIUS,
+ RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE,
+ query->anonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-Anonce");
+ goto fail;
+ }
+
+ if (query->eapol &&
+ !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+ RADIUS_VENDOR_ID_FREERADIUS,
+ RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG,
+ query->eapol, query->eapol_len)) {
+ wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-EAPoL-Key-Msg");
+ goto fail;
+ }
+
+ if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+ goto fail;
+ return 0;
+
+ fail:
+ radius_msg_free(msg);
+ return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_check_acl - Check a specified STA against accept/deny ACLs
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+ struct vlan_description *vlan_id)
+{
+ if (hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac, addr, vlan_id))
+ return HOSTAPD_ACL_ACCEPT;
+
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, addr, vlan_id))
+ return HOSTAPD_ACL_REJECT;
+
+ if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+ return HOSTAPD_ACL_ACCEPT;
+ if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+ return HOSTAPD_ACL_REJECT;
+
+ return HOSTAPD_ACL_PENDING;
+}
+
+
+/**
+ * hostapd_allowed_address - Check whether a specified STA can be authenticated
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @msg: Authentication message
+ * @len: Length of msg in octets
+ * @out.session_timeout: Buffer for returning session timeout (from RADIUS)
+ * @out.acct_interim_interval: Buffer for returning account interval (from
+ * RADIUS)
+ * @out.vlan_id: Buffer for returning VLAN ID
+ * @out.psk: Linked list buffer for returning WPA PSK
+ * @out.identity: Buffer for returning identity (from RADIUS)
+ * @out.radius_cui: Buffer for returning CUI (from RADIUS)
+ * @is_probe_req: Whether this query for a Probe Request frame
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ *
+ * The caller is responsible for properly cloning the returned out->identity and
+ * out->radius_cui and out->psk values.
+ */
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, struct radius_sta *out,
+ int is_probe_req)
+{
+ int res;
+
+ os_memset(out, 0, sizeof(*out));
+
+ res = hostapd_check_acl(hapd, addr, &out->vlan_id);
+ if (res != HOSTAPD_ACL_PENDING)
+ return res;
+
+ if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+#ifdef CONFIG_NO_RADIUS
+ return HOSTAPD_ACL_REJECT;
+#else /* CONFIG_NO_RADIUS */
+ struct hostapd_acl_query_data *query;
+
+ if (is_probe_req) {
+ /* Skip RADIUS queries for Probe Request frames to avoid
+ * excessive load on the authentication server. */
+ return HOSTAPD_ACL_ACCEPT;
+ };
+
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ os_memset(&out->vlan_id, 0, sizeof(out->vlan_id));
+
+ /* Check whether ACL cache has an entry for this station */
+ res = hostapd_acl_cache_get(hapd, addr, out);
+ if (res == HOSTAPD_ACL_ACCEPT ||
+ res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ return res;
+ if (res == HOSTAPD_ACL_REJECT)
+ return HOSTAPD_ACL_REJECT;
+
+ query = hapd->acl_queries;
+ while (query) {
+ if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+ /* pending query in RADIUS retransmit queue;
+ * do not generate a new one */
+ return HOSTAPD_ACL_PENDING;
+ }
+ query = query->next;
+ }
+
+ if (!hapd->conf->radius->auth_server)
+ return HOSTAPD_ACL_REJECT;
+
+ /* No entry in the cache - query external RADIUS server */
+ query = os_zalloc(sizeof(*query));
+ if (!query) {
+ wpa_printf(MSG_ERROR, "malloc for query data failed");
+ return HOSTAPD_ACL_REJECT;
+ }
+ os_get_reltime(&query->timestamp);
+ os_memcpy(query->addr, addr, ETH_ALEN);
+ if (hostapd_radius_acl_query(hapd, addr, query)) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to send Access-Request for ACL query.");
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+
+ query->auth_msg = os_memdup(msg, len);
+ if (!query->auth_msg) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate memory for auth frame.");
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+ query->auth_msg_len = len;
+ query->next = hapd->acl_queries;
+ hapd->acl_queries = query;
+
+ /* Queued data will be processed in hostapd_acl_recv_radius()
+ * when RADIUS server replies to the sent Access-Request. */
+ return HOSTAPD_ACL_PENDING;
+#endif /* CONFIG_NO_RADIUS */
+ }
+
+ return HOSTAPD_ACL_REJECT;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+ struct os_reltime *now)
+{
+ struct hostapd_cached_radius_acl *prev, *entry, *tmp;
+
+ prev = NULL;
+ entry = hapd->acl_cache;
+
+ while (entry) {
+ if (os_reltime_expired(now, &entry->timestamp,
+ RADIUS_ACL_TIMEOUT)) {
+ wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
+ " has expired.", MAC2STR(entry->addr));
+ if (prev)
+ prev->next = entry->next;
+ else
+ hapd->acl_cache = entry->next;
+ hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
+ tmp = entry;
+ entry = entry->next;
+ hostapd_acl_cache_free_entry(tmp);
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+ struct os_reltime *now)
+{
+ struct hostapd_acl_query_data *prev, *entry, *tmp;
+
+ prev = NULL;
+ entry = hapd->acl_queries;
+
+ while (entry) {
+ if (os_reltime_expired(now, &entry->timestamp,
+ RADIUS_ACL_TIMEOUT)) {
+ wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
+ " has expired.", MAC2STR(entry->addr));
+ if (prev)
+ prev->next = entry->next;
+ else
+ hapd->acl_queries = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ hostapd_acl_query_free(tmp);
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+/**
+ * hostapd_acl_expire - ACL cache expiration callback
+ * @hapd: struct hostapd_data *
+ */
+void hostapd_acl_expire(struct hostapd_data *hapd)
+{
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ hostapd_acl_expire_cache(hapd, &now);
+ hostapd_acl_expire_queries(hapd, &now);
+}
+
+
+static void decode_tunnel_passwords(struct hostapd_data *hapd,
+ const u8 *shared_secret,
+ size_t shared_secret_len,
+ struct radius_msg *msg,
+ struct radius_msg *req,
+ struct hostapd_cached_radius_acl *cache)
+{
+ int passphraselen;
+ char *passphrase;
+ size_t i;
+ struct hostapd_sta_wpa_psk_short *psk;
+
+ /*
+ * Decode all tunnel passwords as PSK and save them into a linked list.
+ */
+ for (i = 0; ; i++) {
+ passphrase = radius_msg_get_tunnel_password(
+ msg, &passphraselen, shared_secret, shared_secret_len,
+ req, i);
+ /*
+ * Passphrase is NULL iff there is no i-th Tunnel-Password
+ * attribute in msg.
+ */
+ if (!passphrase)
+ break;
+
+ /*
+ * Passphase should be 8..63 chars (to be hashed with SSID)
+ * or 64 chars hex string (no separate hashing with SSID).
+ */
+
+ if (passphraselen < MIN_PASSPHRASE_LEN ||
+ passphraselen > MAX_PASSPHRASE_LEN + 1)
+ goto free_pass;
+
+ /*
+ * passphrase does not contain the NULL termination.
+ * Add it here as pbkdf2_sha1() requires it.
+ */
+ psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+ if (psk) {
+ if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
+ (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
+ hostapd_logger(hapd, cache->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_WARNING,
+ "invalid hex string (%d chars) in Tunnel-Password",
+ passphraselen);
+ goto skip;
+ } else if (passphraselen <= MAX_PASSPHRASE_LEN) {
+ os_memcpy(psk->passphrase, passphrase,
+ passphraselen);
+ psk->is_passphrase = 1;
+ }
+ psk->next = cache->info.psk;
+ cache->info.psk = psk;
+ psk = NULL;
+ }
+skip:
+ os_free(psk);
+free_pass:
+ os_free(passphrase);
+ }
+}
+
+
+/**
+ * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
+ * was processed here) or RADIUS_RX_UNKNOWN if not.
+ */
+static RadiusRxResult
+hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ struct hostapd_data *hapd = data;
+ struct hostapd_acl_query_data *query, *prev;
+ struct hostapd_cached_radius_acl *cache;
+ struct radius_sta *info;
+ struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+ query = hapd->acl_queries;
+ prev = NULL;
+ while (query) {
+ if (query->radius_id == hdr->identifier)
+ break;
+ prev = query;
+ query = query->next;
+ }
+ if (!query)
+ return RADIUS_RX_UNKNOWN;
+
+ wpa_printf(MSG_DEBUG,
+ "Found matching Access-Request for RADIUS message (id=%d)",
+ query->radius_id);
+
+ if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+ wpa_printf(MSG_INFO,
+ "Incoming RADIUS packet did not have correct authenticator - dropped");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+ hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+ wpa_printf(MSG_DEBUG,
+ "Unknown RADIUS message code %d to ACL query",
+ hdr->code);
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ /* Insert Accept/Reject info into ACL cache */
+ cache = os_zalloc(sizeof(*cache));
+ if (!cache) {
+ wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
+ goto done;
+ }
+ os_get_reltime(&cache->timestamp);
+ os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
+ info = &cache->info;
+ if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+ u8 *buf;
+ size_t len;
+
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+ &info->session_timeout) == 0)
+ cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
+ else
+ cache->accepted = HOSTAPD_ACL_ACCEPT;
+
+ if (radius_msg_get_attr_int32(
+ msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+ &info->acct_interim_interval) == 0 &&
+ info->acct_interim_interval < 60) {
+ wpa_printf(MSG_DEBUG,
+ "Ignored too small Acct-Interim-Interval %d for STA "
+ MACSTR,
+ info->acct_interim_interval,
+ MAC2STR(query->addr));
+ info->acct_interim_interval = 0;
+ }
+
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
+ info->vlan_id.notempty = !!radius_msg_get_vlanid(
+ msg, &info->vlan_id.untagged,
+ MAX_NUM_TAGGED_VLAN, info->vlan_id.tagged);
+
+ decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
+ msg, req, cache);
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+ &buf, &len, NULL) == 0) {
+ info->identity = os_zalloc(len + 1);
+ if (info->identity)
+ os_memcpy(info->identity, buf, len);
+ }
+ if (radius_msg_get_attr_ptr(
+ msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) == 0) {
+ info->radius_cui = os_zalloc(len + 1);
+ if (info->radius_cui)
+ os_memcpy(info->radius_cui, buf, len);
+ }
+
+ if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
+ !info->psk)
+ cache->accepted = HOSTAPD_ACL_REJECT;
+
+ if (info->vlan_id.notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, &info->vlan_id)) {
+ hostapd_logger(hapd, query->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ info->vlan_id.untagged,
+ info->vlan_id.tagged[0] ? "+" : "");
+ os_memset(&info->vlan_id, 0, sizeof(info->vlan_id));
+ }
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+ !info->vlan_id.notempty)
+ cache->accepted = HOSTAPD_ACL_REJECT;
+ } else
+ cache->accepted = HOSTAPD_ACL_REJECT;
+ cache->next = hapd->acl_cache;
+ hapd->acl_cache = cache;
+
+ if (query->radius_psk) {
+ struct sta_info *sta;
+ bool success = cache->accepted == HOSTAPD_ACL_ACCEPT;
+
+ sta = ap_get_sta(hapd, query->addr);
+ if (!sta || !sta->wpa_sm) {
+ wpa_printf(MSG_DEBUG,
+ "No STA/SM entry found for the RADIUS PSK response");
+ goto done;
+ }
+#ifdef NEED_AP_MLME
+ if (success &&
+ (ieee802_11_set_radius_info(hapd, sta, cache->accepted,
+ info) < 0 ||
+ ap_sta_bind_vlan(hapd, sta) < 0))
+ success = false;
+#endif /* NEED_AP_MLME */
+ wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success);
+ } else {
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+ hostapd_drv_set_radius_acl_auth(hapd, query->addr,
+ cache->accepted,
+ info->session_timeout);
+#else /* CONFIG_DRIVER_RADIUS_ACL */
+#ifdef NEED_AP_MLME
+ /* Re-send original authentication frame for 802.11 processing
+ */
+ wpa_printf(MSG_DEBUG,
+ "Re-sending authentication frame after successful RADIUS ACL query");
+ ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
+ NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_DRIVER_RADIUS_ACL */
+ }
+
+ done:
+ if (!prev)
+ hapd->acl_queries = query->next;
+ else
+ prev->next = query->next;
+
+ hostapd_acl_query_free(query);
+
+ return RADIUS_RX_PROCESSED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_acl_init: Initialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int hostapd_acl_init(struct hostapd_data *hapd)
+{
+#ifndef CONFIG_NO_RADIUS
+ if (radius_client_register(hapd->radius, RADIUS_AUTH,
+ hostapd_acl_recv_radius, hapd))
+ return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+ return 0;
+}
+
+
+/**
+ * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ */
+void hostapd_acl_deinit(struct hostapd_data *hapd)
+{
+ struct hostapd_acl_query_data *query, *prev;
+
+#ifndef CONFIG_NO_RADIUS
+ hostapd_acl_cache_free(hapd->acl_cache);
+ hapd->acl_cache = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+ query = hapd->acl_queries;
+ hapd->acl_queries = NULL;
+ while (query) {
+ prev = query;
+ query = query->next;
+ hostapd_acl_query_free(prev);
+ }
+}
+
+
+void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+ struct hostapd_sta_wpa_psk_short *src)
+{
+ if (!psk)
+ return;
+
+ if (src)
+ src->ref++;
+
+ *psk = src;
+}
+
+
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
+{
+ if (psk && psk->ref) {
+ /* This will be freed when the last reference is dropped. */
+ psk->ref--;
+ return;
+ }
+
+ while (psk) {
+ struct hostapd_sta_wpa_psk_short *prev = psk;
+ psk = psk->next;
+ bin_clear_free(prev, sizeof(*prev));
+ }
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+ int key_mgmt, const u8 *anonce,
+ const u8 *eapol, size_t eapol_len)
+{
+ struct hostapd_acl_query_data *query;
+
+ query = os_zalloc(sizeof(*query));
+ if (!query)
+ return;
+
+ query->radius_psk = true;
+ query->akm = key_mgmt;
+ os_get_reltime(&query->timestamp);
+ os_memcpy(query->addr, addr, ETH_ALEN);
+ if (anonce)
+ query->anonce = os_memdup(anonce, WPA_NONCE_LEN);
+ if (eapol) {
+ query->eapol = os_memdup(eapol, eapol_len);
+ query->eapol_len = eapol_len;
+ }
+ if (hostapd_radius_acl_query(hapd, addr, query)) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to send Access-Request for RADIUS PSK/ACL query");
+ hostapd_acl_query_free(query);
+ return;
+ }
+
+ query->next = hapd->acl_queries;
+ hapd->acl_queries = query;
+}
+#endif /* CONFIG_NO_RADIUS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.h
new file mode 100644
index 0000000..22ae1a9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.h
@@ -0,0 +1,43 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+ HOSTAPD_ACL_REJECT = 0,
+ HOSTAPD_ACL_ACCEPT = 1,
+ HOSTAPD_ACL_PENDING = 2,
+ HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+struct radius_sta {
+ u32 session_timeout;
+ u32 acct_interim_interval;
+ struct vlan_description vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk;
+ char *identity;
+ char *radius_cui;
+};
+
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+ struct vlan_description *vlan_id);
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, struct radius_sta *out,
+ int is_probe_req);
+int hostapd_acl_init(struct hostapd_data *hapd);
+void hostapd_acl_deinit(struct hostapd_data *hapd);
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+void hostapd_acl_expire(struct hostapd_data *hapd);
+void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+ struct hostapd_sta_wpa_psk_short *src);
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+ int key_mgmt, const u8 *anonce,
+ const u8 *eapol, size_t eapol_len);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_eht.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_eht.c
new file mode 100644
index 0000000..9a07f75
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_eht.c
@@ -0,0 +1,1139 @@
+/*
+ * hostapd / IEEE 802.11be EHT
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "crypto/dh_groups.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+
+
+static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+ u8 ru;
+ u16 sz = 0;
+
+ if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+ EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+ return 0;
+
+ ru = (ppe_thres_hdr &
+ EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
+ while (ru) {
+ if (ru & 0x1)
+ sz++;
+ ru >>= 1;
+ }
+
+ sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
+ EHT_PPE_THRES_NSS_SHIFT));
+ sz = (sz * 6) + 9;
+ if (sz % 8)
+ sz += 8;
+ sz /= 8;
+
+ return sz;
+}
+
+
+static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
+ u8 he_oper_chwidth, const u8 *he_phy_cap,
+ const u8 *eht_phy_cap)
+{
+ u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+ bool band24, band5, band6;
+ u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+
+ switch (he_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ he_phy_cap_chwidth |=
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_160MHZ:
+ he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_80MHZ:
+ case CONF_OPER_CHWIDTH_USE_HT:
+ he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ break;
+ }
+
+ he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+
+ band24 = mode == HOSTAPD_MODE_IEEE80211B ||
+ mode == HOSTAPD_MODE_IEEE80211G ||
+ mode == NUM_HOSTAPD_MODES;
+ band5 = mode == HOSTAPD_MODE_IEEE80211A ||
+ mode == NUM_HOSTAPD_MODES;
+ band6 = is_6ghz_op_class(opclass);
+
+ if (band24 &&
+ (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
+ return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
+
+ if (band5 &&
+ (he_phy_cap_chwidth &
+ (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
+ return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
+
+ if (band5 &&
+ (he_phy_cap_chwidth &
+ (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
+ sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+ if (band6 &&
+ (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+ EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK))
+ sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+ return sz;
+}
+
+
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ struct eht_capabilities *eht_cap;
+ size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return 0;
+
+ eht_cap = &mode->eht_capab[opmode];
+ if (!eht_cap->eht_supported)
+ return 0;
+
+ len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
+ mode->he_capab[opmode].phy_cap,
+ eht_cap->phy_cap);
+ len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
+ eht_cap->phy_cap);
+
+ return len;
+}
+
+
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ struct eht_capabilities *eht_cap;
+ struct ieee80211_eht_capabilities *cap;
+ size_t mcs_nss_len, ppe_thresh_len;
+ u8 *pos = eid, *length_pos;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return eid;
+
+ eht_cap = &mode->eht_capab[opmode];
+ if (!eht_cap->eht_supported)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ length_pos = pos++;
+ *pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
+
+ cap = (struct ieee80211_eht_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+ cap->mac_cap = host_to_le16(eht_cap->mac_cap);
+ os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
+
+ if (!is_6ghz_op_class(hapd->iconf->op_class))
+ cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
+ ~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
+ if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
+ cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
+ ~EHT_PHYCAP_SU_BEAMFORMER;
+
+ if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
+ cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
+ ~EHT_PHYCAP_SU_BEAMFORMEE;
+
+ if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
+ cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
+ ~EHT_PHYCAP_MU_BEAMFORMER_MASK;
+
+ pos = cap->optional;
+
+ mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
+ hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
+ mode->he_capab[opmode].phy_cap,
+ eht_cap->phy_cap);
+ if (mcs_nss_len) {
+ os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
+ pos += mcs_nss_len;
+ }
+
+ ppe_thresh_len = ieee80211_eht_ppet_size(
+ WPA_GET_LE16(&eht_cap->ppet[0]),
+ eht_cap->phy_cap);
+ if (ppe_thresh_len) {
+ os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
+ pos += ppe_thresh_len;
+ }
+
+ *length_pos = pos - (eid + 2);
+ return pos;
+}
+
+
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_config *conf = hapd->iconf;
+ struct ieee80211_eht_operation *oper;
+ u8 *pos = eid, seg0 = 0, seg1 = 0;
+ enum oper_chan_width chwidth;
+ size_t elen = 1 + 4 + 3;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ if (hapd->iconf->punct_bitmap)
+ elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + elen;
+ *pos++ = WLAN_EID_EXT_EHT_OPERATION;
+
+ oper = (struct ieee80211_eht_operation *) pos;
+ oper->oper_params = EHT_OPER_INFO_PRESENT;
+
+ /* TODO: Fill in appropriate EHT-MCS max Nss information */
+ oper->basic_eht_mcs_nss_set[0] = 0x11;
+ oper->basic_eht_mcs_nss_set[1] = 0x00;
+ oper->basic_eht_mcs_nss_set[2] = 0x00;
+ oper->basic_eht_mcs_nss_set[3] = 0x00;
+
+ if (is_6ghz_op_class(conf->op_class))
+ chwidth = op_class_to_ch_width(conf->op_class);
+ else
+ chwidth = conf->eht_oper_chwidth;
+
+ seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
+
+ switch (chwidth) {
+ case CONF_OPER_CHWIDTH_320MHZ:
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
+ seg1 = seg0;
+ if (hapd->iconf->channel < seg0)
+ seg0 -= 16;
+ else
+ seg0 += 16;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
+ seg1 = seg0;
+ if (hapd->iconf->channel < seg0)
+ seg0 -= 8;
+ else
+ seg0 += 8;
+ break;
+ case CONF_OPER_CHWIDTH_80MHZ:
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
+ break;
+ case CONF_OPER_CHWIDTH_USE_HT:
+ if (seg0)
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
+ break;
+ default:
+ return eid;
+ }
+
+ oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
+ oper->oper_info.ccfs1 = seg1;
+
+ if (hapd->iconf->punct_bitmap) {
+ oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
+ oper->oper_info.disabled_chan_bitmap =
+ host_to_le16(hapd->iconf->punct_bitmap);
+ }
+
+ return pos + elen;
+}
+
+
+static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
+ const u8 *sta_mcs, u8 mcs_count, u8 map_len)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < mcs_count; i++) {
+ ap_mcs += i * 3;
+ sta_mcs += i * 3;
+
+ for (j = 0; j < map_len; j++) {
+ if (((ap_mcs[j] >> 4) & 0xFF) == 0)
+ continue;
+
+ if ((sta_mcs[j] & 0xFF) == 0)
+ continue;
+
+ return true;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "No matching EHT MCS found between AP TX and STA RX");
+ return false;
+}
+
+
+static bool check_valid_eht_mcs(struct hostapd_data *hapd,
+ const u8 *sta_eht_capab,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ const struct ieee80211_eht_capabilities *capab;
+ const u8 *ap_mcs, *sta_mcs;
+ u8 mcs_count = 1;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return true;
+
+ ap_mcs = mode->eht_capab[opmode].mcs;
+ capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
+ sta_mcs = capab->optional;
+
+ if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
+ mode->he_capab[opmode].phy_cap,
+ mode->eht_capab[opmode].phy_cap) ==
+ EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
+ return check_valid_eht_mcs_nss(
+ hapd, ap_mcs, sta_mcs, 1,
+ EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
+
+ switch (hapd->iface->conf->eht_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_320MHZ:
+ mcs_count++;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ case CONF_OPER_CHWIDTH_160MHZ:
+ mcs_count++;
+ break;
+ default:
+ break;
+ }
+
+ return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
+ EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
+}
+
+
+static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
+ u8 opclass, u8 he_oper_chwidth,
+ const u8 *he_cap, const u8 *eht_cap,
+ size_t len)
+{
+ const struct ieee80211_he_capabilities *he_capab;
+ struct ieee80211_eht_capabilities *cap;
+ const u8 *he_phy_cap;
+ size_t cap_len;
+ u16 ppe_thres_hdr;
+
+ he_capab = (const struct ieee80211_he_capabilities *) he_cap;
+ he_phy_cap = he_capab->he_phy_capab_info;
+ cap = (struct ieee80211_eht_capabilities *) eht_cap;
+ cap_len = sizeof(*cap) - sizeof(cap->optional);
+ if (len < cap_len)
+ return true;
+
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
+ he_phy_cap, cap->phy_cap);
+ if (len < cap_len)
+ return true;
+
+ ppe_thres_hdr = len > cap_len + 1 ?
+ WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
+ cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
+
+ return len < cap_len;
+}
+
+
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode,
+ const u8 *he_capab, size_t he_capab_len,
+ const u8 *eht_capab, size_t eht_capab_len)
+{
+ struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
+ enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
+
+ if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
+ !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
+ !eht_capab ||
+ ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
+ hapd->iconf->he_oper_chwidth,
+ he_capab, eht_capab,
+ eht_capab_len) ||
+ !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
+ sta->flags &= ~WLAN_STA_EHT;
+ os_free(sta->eht_capab);
+ sta->eht_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ os_free(sta->eht_capab);
+ sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
+ if (!sta->eht_capab) {
+ sta->eht_capab_len = 0;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_EHT;
+ sta->eht_capab_len = eht_capab_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+ const struct ieee80211_eht_capabilities *src,
+ struct ieee80211_eht_capabilities *dest,
+ size_t len)
+{
+ if (!src || !dest)
+ return;
+
+ if (len > sizeof(*dest))
+ len = sizeof(*dest);
+ /* TODO: mask out unsupported features */
+
+ os_memset(dest, 0, sizeof(*dest));
+ os_memcpy(dest, src, len);
+}
+
+
+u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
+ struct sta_info *info, bool include_mld_id)
+{
+ struct wpabuf *buf;
+ u16 control;
+ u8 *pos = eid;
+ const u8 *ptr;
+ size_t len, slice_len;
+ u8 link_id;
+ u8 common_info_len;
+
+ /*
+ * As the Multi-Link element can exceed the size of 255 bytes need to
+ * first build it and then handle fragmentation.
+ */
+ buf = wpabuf_alloc(1024);
+ if (!buf)
+ return pos;
+
+ /* Multi-Link Control field */
+ control = MULTI_LINK_CONTROL_TYPE_BASIC |
+ BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+ BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+ BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
+ BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+
+ /*
+ * Set the basic Multi-Link common information. Hard code the common
+ * info length to 13 based on the length of the present fields:
+ * Length (1) + MLD address (6) + Link ID (1) +
+ * BSS Parameters Change Count (1) + EML Capabilities (2) +
+ * MLD Capabilities and Operations (2)
+ */
+ common_info_len = 13;
+
+ if (include_mld_id) {
+ /* AP MLD ID */
+ control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+ common_info_len++;
+ }
+
+ wpabuf_put_le16(buf, control);
+
+ wpabuf_put_u8(buf, common_info_len);
+
+ /* Own MLD MAC Address */
+ wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+ /* Own Link ID */
+ wpabuf_put_u8(buf, hapd->mld_link_id);
+
+ /* Currently hard code the BSS Parameters Change Count to 0x1 */
+ wpabuf_put_u8(buf, 0x1);
+
+ wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+ hapd->iface->mld_eml_capa);
+ wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
+
+ wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
+ hapd->iface->mld_mld_capa);
+ wpabuf_put_le16(buf, hapd->iface->mld_mld_capa);
+
+ if (include_mld_id) {
+ wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
+ hapd->conf->mld_id);
+ wpabuf_put_u8(buf, hapd->conf->mld_id);
+ }
+
+ if (!info)
+ goto out;
+
+ /* Add link info for the other links */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &info->mld_info.links[link_id];
+ struct hostapd_data *link_bss;
+
+ /*
+ * control (2) + station info length (1) + MAC address (6) +
+ * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
+ * parameters change counter (1) + station profile length.
+ */
+ const size_t fixed_len = 22;
+ size_t total_len = fixed_len + link->resp_sta_profile_len;
+
+ /* Skip the local one */
+ if (link_id == hapd->mld_link_id || !link->valid)
+ continue;
+
+ link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!link_bss) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Couldn't find link BSS - skip it");
+ continue;
+ }
+
+ /* Per-STA Profile subelement */
+ wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
+
+ if (total_len <= 255)
+ wpabuf_put_u8(buf, total_len);
+ else
+ wpabuf_put_u8(buf, 255);
+
+ /* STA Control */
+ control = (link_id & 0xf) |
+ EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK |
+ EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
+ EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
+ EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+ EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
+ EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
+ wpabuf_put_le16(buf, control);
+
+ /* STA Info */
+
+ /* STA Info Length */
+ wpabuf_put_u8(buf, fixed_len - 2);
+ wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
+ wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
+
+ /* TSF Offset */
+ /*
+ * TODO: Currently setting TSF offset to zero. However, this
+ * information needs to come from the driver.
+ */
+ wpabuf_put_le64(buf, 0);
+
+ /* DTIM Info */
+ wpabuf_put_le16(buf, link_bss->conf->dtim_period);
+
+ /* BSS Parameters Change Count */
+ /* TODO: Currently hard code the BSS Parameters Change Count to
+ * 0x1 */
+ wpabuf_put_u8(buf, 0x1);
+
+ /* Fragment the sub element if needed */
+ if (total_len <= 255) {
+ wpabuf_put_data(buf, link->resp_sta_profile,
+ link->resp_sta_profile_len);
+ } else {
+ ptr = link->resp_sta_profile;
+ len = link->resp_sta_profile_len;
+
+ slice_len = 255 - fixed_len;
+
+ wpabuf_put_data(buf, ptr, slice_len);
+ len -= slice_len;
+ ptr += slice_len;
+
+ while (len) {
+ if (len <= 255)
+ slice_len = len;
+ else
+ slice_len = 255;
+
+ wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT);
+ wpabuf_put_u8(buf, slice_len);
+ wpabuf_put_data(buf, ptr, slice_len);
+
+ len -= slice_len;
+ ptr += slice_len;
+ }
+ }
+ }
+
+out:
+ /* Fragment the Multi-Link element, if needed */
+ len = wpabuf_len(buf);
+ ptr = wpabuf_head(buf);
+
+ if (len <= 254)
+ slice_len = len;
+ else
+ slice_len = 254;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = slice_len + 1;
+ *pos++ = WLAN_EID_EXT_MULTI_LINK;
+ os_memcpy(pos, ptr, slice_len);
+
+ ptr += slice_len;
+ pos += slice_len;
+ len -= slice_len;
+
+ while (len) {
+ if (len <= 255)
+ slice_len = len;
+ else
+ slice_len = 255;
+
+ *pos++ = WLAN_EID_FRAGMENT;
+ *pos++ = slice_len;
+ os_memcpy(pos, ptr, slice_len);
+
+ ptr += slice_len;
+ pos += slice_len;
+ len -= slice_len;
+ }
+
+ wpabuf_free(buf);
+ return pos;
+}
+
+
+struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
+{
+ struct wpabuf *buf = wpabuf_alloc(12);
+
+ if (!buf)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 10);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
+ wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC);
+ wpabuf_put_u8(buf, ETH_ALEN + 1);
+ wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+ return buf;
+}
+
+
+static const u8 *
+sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
+ const u8 *pos, u16 status_code)
+{
+ u16 group;
+ size_t prime_len;
+ struct crypto_ec *ec;
+
+ if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ return pos;
+
+ /* SAE H2E commit message (group, scalar, FFE) */
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE Group is not present");
+ return NULL;
+ }
+
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* TODO: How to parse when the group is unknown? */
+ ec = crypto_ec_init(group);
+ if (!ec) {
+ const struct dh_group *dh = dh_groups_get(group);
+
+ if (!dh) {
+ wpa_printf(MSG_DEBUG, "EHT: Unknown SAE group %u",
+ group);
+ return NULL;
+ }
+
+ prime_len = dh->prime_len;
+ } else {
+ prime_len = crypto_ec_prime_len(ec);
+ }
+
+ wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len);
+
+ /* scalar */
+ pos += prime_len;
+
+ if (ec) {
+ pos += prime_len * 2;
+ crypto_ec_deinit(ec);
+ } else {
+ pos += prime_len;
+ }
+
+ if (pos - mgmt->u.auth.variable > (int) len) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: Too short SAE commit Authentication frame");
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EHT: SAE: Authentication frame elements",
+ pos, (int) len - (pos - mgmt->u.auth.variable));
+
+ return pos;
+}
+
+
+static const u8 *
+sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ const u8 *pos, u16 status_code)
+{
+ struct sta_info *sta;
+
+ if (status_code == WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
+ return pos;
+
+ /* send confirm integer */
+ pos += 2;
+
+ /*
+ * At this stage we should already have an MLD station and actually SA
+ * will be replaced with the MLD MAC address by the driver.
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm");
+ return NULL;
+ }
+
+ if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) {
+ if (sta->sae)
+ wpa_printf(MSG_DEBUG, "SAE: Invalid state=%u",
+ sta->sae->state);
+ else
+ wpa_printf(MSG_DEBUG, "SAE: No SAE context");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
+ sta->sae->tmp->kck_len);
+
+ pos += sta->sae->tmp->kck_len;
+
+ if (pos - mgmt->u.auth.variable > (int) len) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: Too short SAE confirm Authentication frame");
+ return NULL;
+ }
+
+ return pos;
+}
+
+
+static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ u16 status_code = le_to_host16(mgmt->u.auth.status_code);
+ const u8 *pos = mgmt->u.auth.variable;
+
+ /* Skip fixed fields as based on IEE P802.11-REVme/D3.0, Table 9-69
+ * (Presence of fields and elements in Authentications frames) */
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ return pos;
+ case WLAN_AUTH_SAE:
+ if (auth_transaction == 1) {
+ if (status_code == WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EHT: SAE H2E is mandatory for MLD");
+ goto out;
+ }
+
+ return sae_commit_skip_fixed_fields(mgmt, len, pos,
+ status_code);
+ } else if (auth_transaction == 2) {
+ return sae_confirm_skip_fixed_fields(hapd, mgmt, len,
+ pos, status_code);
+ }
+
+ return pos;
+ /* TODO: Support additional algorithms that can be used for MLO */
+ case WLAN_AUTH_FT:
+ case WLAN_AUTH_FILS_SK:
+ case WLAN_AUTH_FILS_SK_PFS:
+ case WLAN_AUTH_FILS_PK:
+ case WLAN_AUTH_PASN:
+ default:
+ break;
+ }
+
+out:
+ wpa_printf(MSG_DEBUG,
+ "TODO: Authentication algorithm %u not supported with MLD",
+ auth_alg);
+ return NULL;
+}
+
+
+const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee802_11_elems elems;
+ const u8 *pos;
+
+ if (!hapd->conf->mld_ap)
+ return NULL;
+
+ len -= offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ pos = auth_skip_fixed_fields(hapd, mgmt, len);
+ if (!pos)
+ return NULL;
+
+ if (ieee802_11_parse_elems(pos,
+ (int)len - (pos - mgmt->u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed parsing Authentication frame");
+ }
+
+ if (!elems.basic_mle || !elems.basic_mle_len)
+ return NULL;
+
+ return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+}
+
+
+static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+ struct mld_info *info)
+{
+ u8 i, link_id;
+
+ if (!info->mld_sta) {
+ wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
+ return 0;
+ }
+
+ /*
+ * Iterate over the links negotiated in the (Re)Association Request
+ * frame and validate that they are indeed valid links in the local AP
+ * MLD.
+ *
+ * While at it, also update the local address for the links in the
+ * mld_info, so it could be easily available for later flows, e.g., for
+ * the RSN Authenticator, etc.
+ */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct hostapd_data *other_hapd;
+
+ if (!info->links[link_id].valid)
+ continue;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (hapd == other_hapd)
+ continue;
+
+ if (other_hapd->conf->mld_ap &&
+ other_hapd->conf->mld_id == hapd->conf->mld_id &&
+ link_id == other_hapd->mld_link_id)
+ break;
+ }
+
+ if (i == hapd->iface->interfaces->count &&
+ link_id != hapd->mld_link_id) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
+ link_id);
+ return -1;
+ }
+
+ if (i < hapd->iface->interfaces->count)
+ os_memcpy(info->links[link_id].local_addr,
+ other_hapd->own_addr,
+ ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+ struct ieee802_11_elems *elems,
+ struct sta_info *sta)
+{
+ struct wpabuf *mlbuf;
+ const struct ieee80211_eht_ml *ml;
+ const struct eht_ml_basic_common_info *common_info;
+ size_t ml_len, common_info_len;
+ struct mld_link_info *link_info;
+ struct mld_info *info = &sta->mld_info;
+ const u8 *pos;
+ int ret = -1;
+ u16 ml_control;
+
+ mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mlbuf)
+ return WLAN_STATUS_SUCCESS;
+
+ ml = wpabuf_head(mlbuf);
+ ml_len = wpabuf_len(mlbuf);
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
+ ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
+ goto out;
+ }
+
+ /* Common Info length and MLD MAC address must always be present */
+ common_info_len = 1 + ETH_ALEN;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+ wpa_printf(MSG_DEBUG, "MLD: Link ID info not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+ wpa_printf(MSG_DEBUG, "MLD: BSS params change not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ common_info_len += 2;
+ } else {
+ wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+ common_info_len += 2;
+
+ } else {
+ wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
+ common_info_len);
+
+ if (sizeof(*ml) + common_info_len > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+ goto out;
+ }
+
+ common_info = (const struct eht_ml_basic_common_info *) ml->variable;
+
+ /* Common information length includes the length octet */
+ if (common_info->len != common_info_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid common info len=%u (expected %zu)",
+ common_info->len, common_info_len);
+ goto out;
+ }
+
+ pos = common_info->variable;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ info->common_info.eml_capa = WPA_GET_LE16(pos);
+ pos += 2;
+ } else {
+ info->common_info.eml_capa = 0;
+ }
+
+ info->common_info.mld_capa = WPA_GET_LE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
+ MAC2STR(info->common_info.mld_addr),
+ info->common_info.eml_capa, info->common_info.mld_capa);
+
+ /* Check the MLD MAC Address */
+ if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
+ ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: MLD address mismatch between authentication ("
+ MACSTR ") and association (" MACSTR ")",
+ MAC2STR(info->common_info.mld_addr),
+ MAC2STR(common_info->mld_addr));
+ goto out;
+ }
+
+ info->links[hapd->mld_link_id].valid = true;
+
+ /* Parse the link info field */
+ ml_len -= sizeof(*ml) + common_info_len;
+
+ while (ml_len > 2) {
+ size_t sub_elem_len = *(pos + 1);
+ size_t sta_info_len;
+ u16 control;
+
+ wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
+ sub_elem_len);
+
+ if (2 + sub_elem_len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid link info len: %zu %zu",
+ 2 + sub_elem_len, ml_len);
+ goto out;
+ }
+
+ if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Skip vendor specific subelement");
+
+ pos += 2 + sub_elem_len;
+ ml_len -= 2 + sub_elem_len;
+ continue;
+ }
+
+ if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Multi-Link element subelement ID=%u",
+ *pos);
+ goto out;
+ }
+
+ /* Skip the subelement ID and the length */
+ pos += 2;
+ ml_len -= 2;
+
+ /* Get the station control field */
+ if (sub_elem_len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Too short Per-STA Profile subelement");
+ goto out;
+ }
+ control = WPA_GET_LE16(pos);
+ link_info = &info->links[control &
+ EHT_PER_STA_CTRL_LINK_ID_MSK];
+ pos += 2;
+ ml_len -= 2;
+ sub_elem_len -= 2;
+
+ if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per-STA complete profile expected");
+ goto out;
+ }
+
+ if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per-STA MAC address not present");
+ goto out;
+ }
+
+ if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+ EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Beacon/DTIM interval not expected");
+ goto out;
+ }
+
+ /* The length octet and the MAC address must be present */
+ sta_info_len = 1 + ETH_ALEN;
+
+ if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
+ if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
+ link_info->nstr_bitmap_len = 2;
+ else
+ link_info->nstr_bitmap_len = 1;
+ }
+
+ sta_info_len += link_info->nstr_bitmap_len;
+
+ if (sta_info_len > ml_len || sta_info_len != *pos ||
+ sta_info_len > sub_elem_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
+ goto out;
+ }
+
+ /* skip the length */
+ pos++;
+ ml_len--;
+
+ /* get the link address */
+ os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
+ wpa_printf(MSG_DEBUG,
+ "MLD: assoc: link id=%u, addr=" MACSTR,
+ control & EHT_PER_STA_CTRL_LINK_ID_MSK,
+ MAC2STR(link_info->peer_addr));
+
+ pos += ETH_ALEN;
+ ml_len -= ETH_ALEN;
+
+ /* Get the NSTR bitmap */
+ if (link_info->nstr_bitmap_len) {
+ os_memcpy(link_info->nstr_bitmap, pos,
+ link_info->nstr_bitmap_len);
+ pos += link_info->nstr_bitmap_len;
+ ml_len -= link_info->nstr_bitmap_len;
+ }
+
+ sub_elem_len -= sta_info_len;
+
+ wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
+ if (sub_elem_len > ml_len)
+ goto out;
+
+ if (sub_elem_len > 2)
+ link_info->capability = WPA_GET_LE16(pos);
+
+ pos += sub_elem_len;
+ ml_len -= sub_elem_len;
+
+ wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
+ ", nstr bitmap len=%lu",
+ control, MAC2STR(link_info->peer_addr),
+ link_info->nstr_bitmap_len);
+
+ link_info->valid = true;
+ }
+
+ if (ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
+ ml_len);
+ goto out;
+ }
+
+ ret = hostapd_mld_validate_assoc_info(hapd, info);
+out:
+ wpabuf_free(mlbuf);
+ if (ret) {
+ os_memset(info, 0, sizeof(*info));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_he.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_he.c
new file mode 100644
index 0000000..548a448
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_he.c
@@ -0,0 +1,570 @@
+/*
+ * hostapd / IEEE 802.11ax HE
+ * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019 John Crispin <john@phrozen.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+ u8 sz = 0, ru;
+
+ if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+ HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+ return 0;
+
+ ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
+ HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
+ /* Count the number of 1 bits in RU Index Bitmask */
+ while (ru) {
+ if (ru & 0x1)
+ sz++;
+ ru >>= 1;
+ }
+
+ /* fixed header of 3 (NSTS) + 4 (RU Index Bitmask) = 7 bits */
+ /* 6 * (NSTS + 1) bits for bit 1 in RU Index Bitmask */
+ sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
+ sz = (sz * 6) + 7;
+ /* PPE Pad to count the number of needed full octets */
+ sz = (sz + 7) / 8;
+
+ return sz;
+}
+
+
+static u8 ieee80211_he_mcs_set_size(const u8 *phy_cap_info)
+{
+ u8 sz = 4;
+
+ if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
+ sz += 4;
+ if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
+ sz += 4;
+
+ return sz;
+}
+
+
+static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
+{
+ struct ieee80211_he_capabilities *cap;
+ size_t cap_len;
+ u8 ppe_thres_hdr;
+
+ cap = (struct ieee80211_he_capabilities *) buf;
+ cap_len = sizeof(*cap) - sizeof(cap->optional);
+ if (len < cap_len)
+ return 1;
+
+ cap_len += ieee80211_he_mcs_set_size(cap->he_phy_capab_info);
+ if (len < cap_len)
+ return 1;
+
+ ppe_thres_hdr = len > cap_len ? buf[cap_len] : 0xff;
+ cap_len += ieee80211_he_ppet_size(ppe_thres_hdr,
+ cap->he_phy_capab_info);
+
+ return len < cap_len;
+}
+
+
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode)
+{
+ struct ieee80211_he_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+ u8 *pos = eid;
+ u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0;
+
+ if (!mode)
+ return eid;
+
+ ie_size = sizeof(*cap) - sizeof(cap->optional);
+ ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
+ mode->he_capab[opmode].phy_cap);
+
+ switch (hapd->iface->conf->he_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ he_oper_chwidth |=
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+ mcs_nss_size += 4;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_160MHZ:
+ he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ mcs_nss_size += 4;
+ /* fall through */
+ case CONF_OPER_CHWIDTH_80MHZ:
+ case CONF_OPER_CHWIDTH_USE_HT:
+ he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ break;
+ default:
+ break;
+ }
+
+ ie_size += mcs_nss_size + ppet_size;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + ie_size;
+ *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
+
+ cap = (struct ieee80211_he_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+
+ os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
+ HE_MAX_MAC_CAPAB_SIZE);
+ os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
+ HE_MAX_PHY_CAPAB_SIZE);
+ os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
+ if (ppet_size)
+ os_memcpy(&cap->optional[mcs_nss_size],
+ mode->he_capab[opmode].ppet, ppet_size);
+
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+ else
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
+ ~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+ else
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
+ ~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+
+ if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+ else
+ cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
+ ~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+
+ cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
+ he_oper_chwidth;
+
+ pos += ie_size;
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_operation *oper;
+ u8 *pos = eid;
+ int oper_size = 6;
+ u32 params = 0;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ oper_size += 5;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + oper_size;
+ *pos++ = WLAN_EID_EXT_HE_OPERATION;
+
+ oper = (struct ieee80211_he_operation *) pos;
+ os_memset(oper, 0, sizeof(*oper));
+
+ if (hapd->iface->conf->he_op.he_default_pe_duration)
+ params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
+ HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+
+ if (hapd->iface->conf->he_op.he_twt_required)
+ params |= HE_OPERATION_TWT_REQUIRED;
+
+ if (hapd->iface->conf->he_op.he_rts_threshold)
+ params |= (hapd->iface->conf->he_op.he_rts_threshold <<
+ HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+ if (hapd->iface->conf->he_op.he_er_su_disable)
+ params |= HE_OPERATION_ER_SU_DISABLE;
+
+ if (hapd->iface->conf->he_op.he_bss_color_disabled ||
+ hapd->cca_in_progress)
+ params |= HE_OPERATION_BSS_COLOR_DISABLED;
+ if (hapd->iface->conf->he_op.he_bss_color_partial)
+ params |= HE_OPERATION_BSS_COLOR_PARTIAL;
+ params |= hapd->iface->conf->he_op.he_bss_color <<
+ HE_OPERATION_BSS_COLOR_OFFSET;
+
+ /* HE minimum required basic MCS and NSS for STAs */
+ oper->he_mcs_nss_set =
+ host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
+
+ /* TODO: conditional MaxBSSID Indicator subfield */
+
+ pos += 6; /* skip the fixed part */
+
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ enum oper_chan_width oper_chwidth =
+ hostapd_get_oper_chwidth(hapd->iconf);
+ u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
+ u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
+ u8 control;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->punct_bitmap) {
+ punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+ hapd->iconf->channel,
+ &oper_chwidth, &seg0, &seg1);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!seg0)
+ seg0 = hapd->iconf->channel;
+
+ params |= HE_OPERATION_6GHZ_OPER_INFO;
+
+ /* 6 GHz Operation Information field
+ * IEEE Std 802.11ax-2021, 9.4.2.249 HE Operation element,
+ * Figure 9-788k
+ */
+ *pos++ = hapd->iconf->channel; /* Primary Channel */
+
+ /* Control:
+ * bits 0-1: Channel Width
+ * bit 2: Duplicate Beacon
+ * bits 3-5: Regulatory Info
+ */
+ /* Channel Width */
+ if (seg1)
+ control = 3;
+ else
+ control = center_idx_to_bw_6ghz(seg0);
+ if (hapd->iconf->he_6ghz_reg_pwr_type == 1)
+ control |= HE_6GHZ_STANDARD_POWER_AP <<
+ HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+ else
+ control |= HE_6GHZ_INDOOR_AP <<
+ HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+ *pos++ = control;
+
+ /* Channel Center Freq Seg0/Seg1 */
+ if (oper_chwidth == 2) {
+ /*
+ * Seg 0 indicates the channel center frequency index of
+ * the 160 MHz channel.
+ */
+ seg1 = seg0;
+ if (hapd->iconf->channel < seg0)
+ seg0 -= 8;
+ else
+ seg0 += 8;
+ }
+
+ *pos++ = seg0;
+ *pos++ = seg1;
+ /* Minimum Rate */
+ *pos++ = 6; /* TODO: what should be set here? */
+ }
+
+ oper->he_oper_params = host_to_le32(params);
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_mu_edca_parameter_set *edca;
+ u8 *pos;
+ size_t i;
+
+ pos = (u8 *) &hapd->iface->conf->he_mu_edca;
+ for (i = 0; i < sizeof(*edca); i++) {
+ if (pos[i])
+ break;
+ }
+ if (i == sizeof(*edca))
+ return eid; /* no MU EDCA Parameters configured */
+
+ pos = eid;
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*edca);
+ *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
+
+ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+
+ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ pos, sizeof(*edca));
+
+ pos += sizeof(*edca);
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_spatial_reuse *spr;
+ u8 *pos = eid, *spr_param;
+ u8 sz = 1;
+
+ if (!hapd->iface->conf->spr.sr_control)
+ return eid;
+
+ if (hapd->iface->conf->spr.sr_control &
+ SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
+ sz++;
+
+ if (hapd->iface->conf->spr.sr_control &
+ SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
+ sz += 18;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sz;
+ *pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
+
+ spr = (struct ieee80211_spatial_reuse *) pos;
+ os_memset(spr, 0, sizeof(*spr));
+
+ spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
+ pos++;
+ spr_param = spr->params;
+ if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
+ *spr_param++ =
+ hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
+ pos++;
+ }
+ if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
+ *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
+ *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
+ os_memcpy(spr_param,
+ hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
+ spr_param += 8;
+ os_memcpy(spr_param,
+ hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
+ pos += 18;
+ }
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_config *conf = hapd->iface->conf;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ struct he_capabilities *he_cap;
+ struct ieee80211_he_6ghz_band_cap *cap;
+ u16 capab;
+ u8 *pos;
+
+ if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
+ !is_6ghz_freq(hapd->iface->freq))
+ return eid;
+
+ he_cap = &mode->he_capab[IEEE80211_MODE_AP];
+ capab = he_cap->he_6ghz_capa & HE_6GHZ_BAND_CAP_MIN_MPDU_START;
+ capab |= (conf->he_6ghz_max_ampdu_len_exp <<
+ HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT) &
+ HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK;
+ capab |= (conf->he_6ghz_max_mpdu <<
+ HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT) &
+ HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK;
+ capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
+ if (conf->he_6ghz_rx_ant_pat)
+ capab |= HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS;
+ if (conf->he_6ghz_tx_ant_pat)
+ capab |= HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS;
+
+ pos = eid;
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*cap);
+ *pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
+
+ cap = (struct ieee80211_he_6ghz_band_cap *) pos;
+ cap->capab = host_to_le16(capab);
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+ const struct ieee80211_he_capabilities *he_cap,
+ struct ieee80211_he_capabilities *neg_he_cap,
+ size_t he_capab_len)
+{
+ if (!he_cap)
+ return;
+
+ if (he_capab_len > sizeof(*neg_he_cap))
+ he_capab_len = sizeof(*neg_he_cap);
+ /* TODO: mask out unsupported features */
+
+ os_memcpy(neg_he_cap, he_cap, he_capab_len);
+}
+
+
+static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
+ enum ieee80211_op_mode opmode)
+{
+ u16 sta_rx_mcs_set, ap_tx_mcs_set;
+ u8 mcs_count = 0;
+ const u16 *ap_mcs_set, *sta_mcs_set;
+ int i;
+
+ if (!hapd->iface->current_mode)
+ return 1;
+ ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
+ sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
+ sta_he_capab)->optional;
+
+ /*
+ * Disable HE capabilities for STAs for which there is not even a single
+ * allowed MCS in any supported number of streams, i.e., STA is
+ * advertising 3 (not supported) as HE MCS rates for all supported
+ * band/stream cases.
+ */
+ switch (hapd->iface->conf->he_oper_chwidth) {
+ case CONF_OPER_CHWIDTH_80P80MHZ:
+ mcs_count = 3;
+ break;
+ case CONF_OPER_CHWIDTH_160MHZ:
+ mcs_count = 2;
+ break;
+ default:
+ mcs_count = 1;
+ break;
+ }
+
+ for (i = 0; i < mcs_count; i++) {
+ int j;
+
+ /* AP Tx MCS map vs. STA Rx MCS map */
+ sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
+ ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
+ &ap_mcs_set[(i * 2) + 1]);
+
+ for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
+ if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
+ continue;
+
+ if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
+ continue;
+
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "No matching HE MCS found between AP TX and STA RX");
+
+ return 0;
+}
+
+
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode, const u8 *he_capab,
+ size_t he_capab_len)
+{
+ if (!he_capab || !(sta->flags & WLAN_STA_WMM) ||
+ !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax ||
+ !check_valid_he_mcs(hapd, he_capab, opmode) ||
+ ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
+ he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
+ sta->flags &= ~WLAN_STA_HE;
+ os_free(sta->he_capab);
+ sta->he_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->he_capab) {
+ sta->he_capab =
+ os_zalloc(sizeof(struct ieee80211_he_capabilities));
+ if (!sta->he_capab)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_HE;
+ os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
+ os_memcpy(sta->he_capab, he_capab, he_capab_len);
+ sta->he_capab_len = he_capab_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_6ghz_capab)
+{
+ if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
+ hapd->conf->disable_11ax ||
+ !is_6ghz_op_class(hapd->iconf->op_class)) {
+ sta->flags &= ~WLAN_STA_6GHZ;
+ os_free(sta->he_6ghz_capab);
+ sta->he_6ghz_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->he_6ghz_capab) {
+ sta->he_6ghz_capab =
+ os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
+ if (!sta->he_6ghz_capab)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_6GHZ;
+ os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
+ sizeof(struct ieee80211_he_6ghz_band_cap));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
+ enum ieee80211_op_mode mode)
+{
+ u8 *mac_cap;
+
+ if (!hapd->iface->current_mode ||
+ !hapd->iface->current_mode->he_capab[mode].he_supported ||
+ !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ return 0;
+
+ mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
+
+ return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
+ hapd->iface->conf->he_op.he_twt_responder;
+}
+
+
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid)
+{
+ if (!hapd->cca_in_progress)
+ return eid;
+
+ /* BSS Color Change Announcement element */
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 3;
+ *eid++ = WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT;
+ *eid++ = hapd->cca_count; /* Color Switch Countdown */
+ *eid++ = hapd->cca_color; /* New BSS Color Information */
+
+ return eid;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_ht.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_ht.c
new file mode 100644
index 0000000..0b166d9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_ht.c
@@ -0,0 +1,551 @@
+/*
+ * hostapd / IEEE 802.11n HT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
+
+
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_ht_capabilities *cap;
+ u8 *pos = eid;
+
+ if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+ hapd->conf->disable_11n || is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+
+ *pos++ = WLAN_EID_HT_CAP;
+ *pos++ = sizeof(*cap);
+
+ cap = (struct ieee80211_ht_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+ cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
+ cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
+ os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
+ 16);
+
+ /* TODO: ht_extended_capabilities (now fully disabled) */
+ /* TODO: tx_bf_capability_info (now fully disabled) */
+ /* TODO: asel_capabilities (now fully disabled) */
+
+ pos += sizeof(*cap);
+
+ if (hapd->iconf->obss_interval) {
+ struct ieee80211_obss_scan_parameters *scan_params;
+
+ *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
+ *pos++ = sizeof(*scan_params);
+
+ scan_params = (struct ieee80211_obss_scan_parameters *) pos;
+ os_memset(scan_params, 0, sizeof(*scan_params));
+ scan_params->width_trigger_scan_interval =
+ host_to_le16(hapd->iconf->obss_interval);
+
+ /* Fill in default values for remaining parameters
+ * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
+ scan_params->scan_passive_dwell =
+ host_to_le16(20);
+ scan_params->scan_active_dwell =
+ host_to_le16(10);
+ scan_params->scan_passive_total_per_channel =
+ host_to_le16(200);
+ scan_params->scan_active_total_per_channel =
+ host_to_le16(20);
+ scan_params->channel_transition_delay_factor =
+ host_to_le16(5);
+ scan_params->scan_activity_threshold =
+ host_to_le16(25);
+
+ pos += sizeof(*scan_params);
+ }
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_ht_operation *oper;
+ le32 vht_capabilities_info;
+ u8 *pos = eid;
+ u8 chwidth;
+
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+ is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+
+ *pos++ = WLAN_EID_HT_OPERATION;
+ *pos++ = sizeof(*oper);
+
+ oper = (struct ieee80211_ht_operation *) pos;
+ os_memset(oper, 0, sizeof(*oper));
+
+ oper->primary_chan = hapd->iconf->channel;
+ oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
+ if (hapd->iconf->secondary_channel == 1)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+ if (hapd->iconf->secondary_channel == -1)
+ oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+
+ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
+ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
+ oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
+ }
+
+ pos += sizeof(*oper);
+
+ return pos;
+}
+
+
+/*
+op_mode
+Set to 0 (HT pure) under the following conditions
+ - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+ - all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+ in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+ however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+*/
+int hostapd_ht_operation_update(struct hostapd_iface *iface)
+{
+ u16 cur_op_mode, new_op_mode;
+ int op_mode_changes = 0;
+
+ if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
+ __func__, iface->ht_op_mode);
+
+ if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
+ && iface->num_sta_ht_no_gf) {
+ iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
+ op_mode_changes++;
+ } else if ((iface->ht_op_mode &
+ HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
+ iface->num_sta_ht_no_gf == 0) {
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
+ op_mode_changes++;
+ }
+
+ if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
+ (iface->num_sta_no_ht || iface->olbc_ht)) {
+ iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
+ op_mode_changes++;
+ } else if ((iface->ht_op_mode &
+ HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
+ (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
+ op_mode_changes++;
+ }
+
+ if (iface->num_sta_no_ht)
+ new_op_mode = HT_PROT_NON_HT_MIXED;
+ else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
+ new_op_mode = HT_PROT_20MHZ_PROTECTION;
+ else if (iface->olbc_ht)
+ new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
+ else
+ new_op_mode = HT_PROT_NO_PROTECTION;
+
+ cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
+ if (cur_op_mode != new_op_mode) {
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
+ iface->ht_op_mode |= new_op_mode;
+ op_mode_changes++;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
+ __func__, iface->ht_op_mode, op_mode_changes);
+
+ return op_mode_changes;
+}
+
+
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+ int pri_freq, sec_freq;
+ int affected_start, affected_end;
+ int pri = 2407 + 5 * channel;
+
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return 1;
+
+ pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+
+ if (iface->conf->secondary_channel > 0)
+ sec_freq = pri_freq + 20;
+ else
+ sec_freq = pri_freq - 20;
+
+ affected_start = (pri_freq + sec_freq) / 2 - 25;
+ affected_end = (pri_freq + sec_freq) / 2 + 25;
+ if ((pri < affected_start || pri > affected_end))
+ return 1; /* not within affected channel range */
+
+ wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+ wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+ return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct ieee80211_2040_bss_coex_ie *bc_ie;
+ struct ieee80211_2040_intol_chan_report *ic_report;
+ int is_ht40_allowed = 1;
+ int i;
+ const u8 *start = (const u8 *) mgmt;
+ const u8 *data = start + IEEE80211_HDRLEN + 2;
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG,
+ "HT: Received 20/40 BSS Coexistence Management frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+ mgmt->u.action.u.public_action.action);
+
+ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
+ return;
+ }
+
+ if (iface->conf->noscan || iface->conf->no_ht_coex)
+ return;
+
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore too short 20/40 BSS Coexistence Management frame");
+ return;
+ }
+
+ /* 20/40 BSS Coexistence element */
+ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+ if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+ bc_ie->length < 1) {
+ wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
+ bc_ie->element_id, bc_ie->length);
+ return;
+ }
+ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Coexistence element");
+ return;
+ }
+ data += 2 + bc_ie->length;
+
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
+ bc_ie->coex_param,
+ (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
+ (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
+ (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
+ (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
+ (bc_ie->coex_param & BIT(4)) ?
+ "[OBSSScanExemptionGrant]" : "",
+ (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
+ "[Reserved]" : "");
+
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+ /* Intra-BSS communication prohibiting 20/40 MHz BSS operation
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
+ return;
+ }
+
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "20 MHz BSS width request bit is set in BSS coexistence information field");
+ is_ht40_allowed = 0;
+ }
+
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+ /* Inter-BSS communication prohibiting 20/40 MHz BSS operation
+ */
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "40 MHz intolerant bit is set in BSS coexistence information field");
+ is_ht40_allowed = 0;
+ }
+
+ /* 20/40 BSS Intolerant Channel Report element (zero or more times) */
+ while (start + len - data >= 3 &&
+ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+ u8 ielen = data[1];
+
+ if (ielen > start + len - data - 2) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Intolerant Channel Report element");
+ return;
+ }
+ ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Intolerant Channel Report: Operating Class %u",
+ ic_report->op_class);
+
+ /* Go through the channel report to find any BSS there in the
+ * affected channel range */
+ for (i = 0; i < ielen - 1; i++) {
+ u8 chan = ic_report->variable[i];
+
+ if (chan == iface->conf->channel)
+ continue; /* matching own primary channel */
+ if (is_40_allowed(iface, chan))
+ continue; /* not within affected channels */
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "20_40_INTOLERANT channel %d reported",
+ chan);
+ is_ht40_allowed = 0;
+ }
+
+ data += 2 + ielen;
+ }
+ wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
+ is_ht40_allowed, iface->num_sta_ht40_intolerant);
+
+ if (!is_ht40_allowed &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ if (iface->conf->secondary_channel) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Switching to 20 MHz operation");
+ iface->conf->secondary_channel = 0;
+ ieee802_11_set_beacons(iface);
+ }
+ if (!iface->num_sta_ht40_intolerant &&
+ iface->conf->obss_interval) {
+ unsigned int delay_time;
+ delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+ iface->conf->obss_interval;
+ eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+ NULL);
+ eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+ hapd->iface, NULL);
+ wpa_printf(MSG_DEBUG,
+ "Reschedule HT 20/40 timeout to occur in %u seconds",
+ delay_time);
+ }
+ }
+}
+
+
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab)
+{
+ /*
+ * Disable HT caps for STAs associated to no-HT BSSes, or for stations
+ * that did not specify a valid WMM IE in the (Re)Association Request
+ * frame.
+ */
+ if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
+ !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
+ sta->flags &= ~WLAN_STA_HT;
+ os_free(sta->ht_capabilities);
+ sta->ht_capabilities = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (sta->ht_capabilities == NULL) {
+ sta->ht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+ if (sta->ht_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_HT;
+ os_memcpy(sta->ht_capabilities, ht_capab,
+ sizeof(struct ieee80211_ht_capabilities));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+{
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return;
+
+ if (iface->conf->noscan || iface->conf->no_ht_coex)
+ return;
+
+ wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ " in Association Request", MAC2STR(sta->addr));
+
+ if (sta->ht40_intolerant_set)
+ return;
+
+ sta->ht40_intolerant_set = 1;
+ iface->num_sta_ht40_intolerant++;
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+
+ if (iface->conf->secondary_channel &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ iface->conf->secondary_channel = 0;
+ ieee802_11_set_beacons(iface);
+ }
+}
+
+
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
+{
+ if (!sta->ht40_intolerant_set)
+ return;
+
+ sta->ht40_intolerant_set = 0;
+ iface->num_sta_ht40_intolerant--;
+
+ if (iface->num_sta_ht40_intolerant == 0 &&
+ (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+ iface->conf->obss_interval;
+ wpa_printf(MSG_DEBUG,
+ "HT: Start 20->40 MHz transition timer (%d seconds)",
+ delay_time);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+ eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+ iface, NULL);
+ }
+}
+
+
+static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ u16 ht_capab;
+
+ ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
+ wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
+ "0x%04x", MAC2STR(sta->addr), ht_capab);
+ if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
+ if (!sta->no_ht_gf_set) {
+ sta->no_ht_gf_set = 1;
+ hapd->iface->num_sta_ht_no_gf++;
+ }
+ wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
+ "of non-gf stations %d",
+ __func__, MAC2STR(sta->addr),
+ hapd->iface->num_sta_ht_no_gf);
+ }
+ if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
+ if (!sta->ht_20mhz_set) {
+ sta->ht_20mhz_set = 1;
+ hapd->iface->num_sta_ht_20mhz++;
+ }
+ wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
+ "20MHz HT STAs %d",
+ __func__, MAC2STR(sta->addr),
+ hapd->iface->num_sta_ht_20mhz);
+ }
+#ifndef CONFIG_24G_BW_SWITCH
+ if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+ ht40_intolerant_add(hapd->iface, sta);
+#endif /* CONFIG_24G_BW_SWITCH */
+}
+
+
+static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!sta->no_ht_set) {
+ sta->no_ht_set = 1;
+ hapd->iface->num_sta_no_ht++;
+ }
+ if (hapd->iconf->ieee80211n) {
+ wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
+ "non-HT stations %d",
+ __func__, MAC2STR(sta->addr),
+ hapd->iface->num_sta_no_ht);
+ }
+}
+
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
+ update_sta_ht(hapd, sta);
+ else
+ update_sta_no_ht(hapd, sta);
+
+ if (hostapd_ht_operation_update(hapd->iface) > 0)
+ ieee802_11_set_beacons(hapd->iface);
+}
+
+
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+ struct ieee80211_ht_capabilities *ht_cap,
+ struct ieee80211_ht_capabilities *neg_ht_cap)
+{
+ u16 cap;
+
+ if (ht_cap == NULL)
+ return;
+ os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
+ cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
+
+ /*
+ * Mask out HT features we don't support, but don't overwrite
+ * non-symmetric features like STBC and SMPS. Just because
+ * we're not in dynamic SMPS mode the STA might still be.
+ */
+ cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
+ HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
+
+ /*
+ * STBC needs to be handled specially
+ * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+ * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+ */
+ if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
+ cap &= ~HT_CAP_INFO_TX_STBC;
+ if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
+ cap &= ~HT_CAP_INFO_RX_STBC_MASK;
+
+ neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
+}
+
+
+void ap_ht2040_timeout(void *eloop_data, void *user_data)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
+
+ iface->conf->secondary_channel = iface->secondary_ch;
+ ieee802_11_set_beacons(iface);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_shared.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_shared.c
new file mode 100644
index 0000000..0f61e58
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_shared.c
@@ -0,0 +1,1137 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "dpp_hostapd.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid)
+{
+ u8 *pos = eid;
+ u32 timeout, tu;
+ struct os_reltime now, passed;
+
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &sta->sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (hapd->conf->assoc_sa_query_max_timeout > tu)
+ timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+ else
+ timeout = 0;
+ if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+ timeout++; /* add some extra time for local timers */
+ WPA_PUT_LE32(pos, timeout);
+ pos += 4;
+
+ return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id)
+{
+#ifdef CONFIG_OCV
+ struct sta_info *sta;
+#endif /* CONFIG_OCV */
+ struct ieee80211_mgmt *mgmt;
+ u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+#ifdef CONFIG_OCV
+ sta = ap_get_sta(hapd, addr);
+ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in SA Query Request");
+ return;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->oci_freq_override_saquery_req) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ hapd->conf->oci_freq_override_saquery_req);
+ ci.frequency =
+ hapd->conf->oci_freq_override_saquery_req;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for OCI element in SA Query Request");
+ return;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(oci_ie);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
+ if (!mgmt) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate buffer for SA Query Response frame");
+ os_free(oci_ie);
+ return;
+ }
+
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = mgmt->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+ if (oci_ie_len > 0) {
+ os_memcpy(end, oci_ie, oci_ie_len);
+ end += oci_ie_len;
+ }
+#endif /* CONFIG_OCV */
+ if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0, NULL, 0, 0)
+ < 0)
+ wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
+
+ os_free(mgmt);
+ os_free(oci_ie);
+}
+
+
+static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+ const u8 *sa, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ struct ieee80211_mgmt *resp;
+ u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+ "from unassociated STA " MACSTR, MAC2STR(sa));
+ return;
+ }
+
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in SA Query Response");
+ return;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->oci_freq_override_saquery_resp) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ hapd->conf->oci_freq_override_saquery_resp);
+ ci.frequency =
+ hapd->conf->oci_freq_override_saquery_resp;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for for OCI element in SA Query Response");
+ return;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(oci_ie);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ resp = os_zalloc(sizeof(*resp) + oci_ie_len);
+ if (!resp) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate buffer for SA Query Response frame");
+ os_free(oci_ie);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+ MACSTR, MAC2STR(sa));
+
+ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp->da, sa, ETH_ALEN);
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ resp->u.action.category = WLAN_ACTION_SA_QUERY;
+ resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = resp->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+ if (oci_ie_len > 0) {
+ os_memcpy(end, oci_ie, oci_ie_len);
+ end += oci_ie_len;
+ }
+#endif /* CONFIG_OCV */
+ if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0, NULL, 0, 0)
+ < 0)
+ wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
+
+ os_free(resp);
+ os_free(oci_ie);
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct sta_info *sta;
+ int i;
+ const u8 *sa = mgmt->sa;
+ const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
+ const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
+
+ if (((const u8 *) mgmt) + len <
+ mgmt->u.action.u.sa_query_resp.variable) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.11: Too short SA Query Action frame (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+ if (is_multicast_ether_addr(mgmt->da)) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.11: Ignore group-addressed SA Query frame (A1=" MACSTR " A2=" MACSTR ")",
+ MAC2STR(mgmt->da), MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta = ap_get_sta(hapd, sa);
+
+#ifdef CONFIG_OCV
+ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct ieee802_11_elems elems;
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+ size_t ies_len;
+ const u8 *ies;
+
+ ies = mgmt->u.action.u.sa_query_resp.variable;
+ ies_len = len - (ies - (u8 *) mgmt);
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "SA Query: Failed to parse elements");
+ return;
+ }
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in SA Query Action frame");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sta->wpa_sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) !=
+ OCI_SUCCESS) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+ MACSTR " frame=saquery%s error=%s",
+ MAC2STR(sa),
+ action_type == WLAN_SA_QUERY_REQUEST ?
+ "req" : "resp", ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (action_type == WLAN_SA_QUERY_REQUEST) {
+ if (sta)
+ sta->post_csa_sa_query = 0;
+ ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+ return;
+ }
+
+ if (action_type != WLAN_SA_QUERY_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+ "Action %d", action_type);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ /* MLME-SAQuery.confirm */
+
+ if (sta == NULL || sta->sa_query_trans_id == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+ "pending SA Query request found");
+ return;
+ }
+
+ for (i = 0; i < sta->sa_query_count; i++) {
+ if (os_memcmp(sta->sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= sta->sa_query_count) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Reply to pending SA Query received");
+ ap_sta_stop_sa_query(hapd, sta);
+}
+
+
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
+ bool mbssid_complete)
+{
+ *pos = 0x00;
+
+ switch (idx) {
+ case 0: /* Bits 0-7 */
+ if (hapd->iconf->obss_interval)
+ *pos |= 0x01; /* Bit 0 - Coexistence management */
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
+ *pos |= 0x04; /* Bit 2 - Extended Channel Switching */
+ break;
+ case 1: /* Bits 8-15 */
+ if (hapd->conf->proxy_arp)
+ *pos |= 0x10; /* Bit 12 - Proxy ARP */
+ if (hapd->conf->coloc_intf_reporting) {
+ /* Bit 13 - Collocated Interference Reporting */
+ *pos |= 0x20;
+ }
+ break;
+ case 2: /* Bits 16-23 */
+ if (hapd->conf->wnm_sleep_mode)
+ *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ if (hapd->conf->bss_transition)
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+ if (hapd->iconf->mbssid)
+ *pos |= 0x40; /* Bit 22 - Multiple BSSID */
+ break;
+ case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM_AP
+ *pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM_AP */
+ if (hapd->conf->time_advertisement == 2)
+ *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+ if (hapd->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
+ break;
+ case 4: /* Bits 32-39 */
+ if (hapd->conf->qos_map_set_len)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
+ if (hapd->conf->tdls & TDLS_PROHIBIT)
+ *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
+ /* Bit 39 - TDLS Channel Switching Prohibited */
+ *pos |= 0x80;
+ }
+ break;
+ case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
+ break;
+ case 6: /* Bits 48-55 */
+ if (hapd->conf->ssid.utf8_ssid)
+ *pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+ break;
+ case 7: /* Bits 56-63 */
+ break;
+ case 8: /* Bits 64-71 */
+ if (hapd->conf->ftm_responder)
+ *pos |= 0x40; /* Bit 70 - FTM responder */
+ if (hapd->conf->ftm_initiator)
+ *pos |= 0x80; /* Bit 71 - FTM initiator */
+ break;
+ case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
+ wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+ *pos |= 0x01;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211AX
+ if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
+ *pos |= 0x40; /* Bit 78 - TWT responder */
+#endif /* CONFIG_IEEE80211AX */
+ break;
+ case 10: /* Bits 80-87 */
+#ifdef CONFIG_SAE
+ if (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
+ int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
+
+ if (in_use)
+ *pos |= 0x02; /* Bit 81 - SAE Password
+ * Identifiers In Use */
+ if (in_use == 2)
+ *pos |= 0x04; /* Bit 82 - SAE Password
+ * Identifiers Used Exclusively */
+ }
+#endif /* CONFIG_SAE */
+ if (hapd->conf->beacon_prot &&
+ (hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+ *pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
+ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
+ *pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+ if (mbssid_complete)
+ *pos |= 0x01; /* Bit 80 - Complete List of NonTxBSSID
+ * Profiles */
+ break;
+ case 11: /* Bits 88-95 */
+#ifdef CONFIG_SAE_PK
+ if (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ hostapd_sae_pk_exclusively(hapd->conf))
+ *pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
+#endif /* CONFIG_SAE_PK */
+ break;
+ }
+}
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+ bool mbssid_complete)
+{
+ u8 *pos = eid;
+ u8 len = EXT_CAPA_MAX_LEN, i;
+
+ if (len < hapd->iface->extended_capa_len)
+ len = hapd->iface->extended_capa_len;
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = len;
+ for (i = 0; i < len; i++, pos++) {
+ hostapd_ext_capab_byte(hapd, pos, i, mbssid_complete);
+
+ if (i < hapd->iface->extended_capa_len) {
+ *pos &= ~hapd->iface->extended_capa_mask[i];
+ *pos |= hapd->iface->extended_capa[i];
+ }
+
+ if (i < EXT_CAPA_MAX_LEN) {
+ *pos &= ~hapd->conf->ext_capa_mask[i];
+ *pos |= hapd->conf->ext_capa[i];
+ }
+
+ /* Clear bits 83 and 22 if EMA and MBSSID are not enabled
+ * otherwise association fails with some clients */
+ if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
+ *pos &= ~0x08;
+ if (i == 2 && !hapd->iconf->mbssid)
+ *pos &= ~0x40;
+ }
+
+ while (len > 0 && eid[1 + len] == 0) {
+ len--;
+ eid[1] = len;
+ }
+ if (len == 0)
+ return eid;
+
+ return eid + 2 + len;
+}
+
+
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = hapd->conf->qos_map_set_len;
+
+ if (!len)
+ return eid;
+
+ *pos++ = WLAN_EID_QOS_MAP_SET;
+ *pos++ = len;
+ os_memcpy(pos, hapd->conf->qos_map_set, len);
+ pos += len;
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_INTERWORKING;
+ len = pos++;
+
+ *pos = hapd->conf->access_network_type;
+ if (hapd->conf->internet)
+ *pos |= INTERWORKING_ANO_INTERNET;
+ if (hapd->conf->asra)
+ *pos |= INTERWORKING_ANO_ASRA;
+ if (hapd->conf->esr)
+ *pos |= INTERWORKING_ANO_ESR;
+ if (hapd->conf->uesa)
+ *pos |= INTERWORKING_ANO_UESA;
+ pos++;
+
+ if (hapd->conf->venue_info_set) {
+ *pos++ = hapd->conf->venue_group;
+ *pos++ = hapd->conf->venue_type;
+ }
+
+ if (!is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+ /* TODO: Separate configuration for ANQP? */
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_ADV_PROTO;
+ *pos++ = 2;
+ *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+ *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+ unsigned int i, count;
+
+ if (!hapd->conf->interworking ||
+ hapd->conf->roaming_consortium == NULL ||
+ hapd->conf->roaming_consortium_count == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+ len = pos++;
+
+ /* Number of ANQP OIs (in addition to the max 3 listed here) */
+ if (hapd->conf->roaming_consortium_count > 3 + 255)
+ *pos++ = 255;
+ else if (hapd->conf->roaming_consortium_count > 3)
+ *pos++ = hapd->conf->roaming_consortium_count - 3;
+ else
+ *pos++ = 0;
+
+ /* OU #1 and #2 Lengths */
+ *pos = hapd->conf->roaming_consortium[0].len;
+ if (hapd->conf->roaming_consortium_count > 1)
+ *pos |= hapd->conf->roaming_consortium[1].len << 4;
+ pos++;
+
+ if (hapd->conf->roaming_consortium_count > 3)
+ count = 3;
+ else
+ count = hapd->conf->roaming_consortium_count;
+
+ for (i = 0; i < count; i++) {
+ os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+ hapd->conf->roaming_consortium[i].len);
+ pos += hapd->conf->roaming_consortium[i].len;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ if (hapd->time_adv == NULL &&
+ hostapd_update_time_adv(hapd) < 0)
+ return eid;
+
+ if (hapd->time_adv == NULL)
+ return eid;
+
+ os_memcpy(eid, wpabuf_head(hapd->time_adv),
+ wpabuf_len(hapd->time_adv));
+ eid += wpabuf_len(hapd->time_adv);
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+ size_t len;
+
+ if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
+ return eid;
+
+ len = os_strlen(hapd->conf->time_zone);
+
+ *eid++ = WLAN_EID_TIME_ZONE;
+ *eid++ = len;
+ os_memcpy(eid, hapd->conf->time_zone, len);
+ eid += len;
+
+ return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+ const int elen = 2 + 1 + 10 + 5 + 1;
+ struct os_time t;
+ struct os_tm tm;
+ u8 *pos;
+
+ if (hapd->conf->time_advertisement != 2)
+ return 0;
+
+ if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+ return -1;
+
+ if (!hapd->time_adv) {
+ hapd->time_adv = wpabuf_alloc(elen);
+ if (hapd->time_adv == NULL)
+ return -1;
+ pos = wpabuf_put(hapd->time_adv, elen);
+ } else
+ pos = wpabuf_mhead_u8(hapd->time_adv);
+
+ *pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+ *pos++ = 1 + 10 + 5 + 1;
+
+ *pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+ /* Time Value at TSF 0 */
+ /* FIX: need to calculate this based on the current TSF value */
+ WPA_PUT_LE16(pos, tm.year); /* Year */
+ pos += 2;
+ *pos++ = tm.month; /* Month */
+ *pos++ = tm.day; /* Day of month */
+ *pos++ = tm.hour; /* Hours */
+ *pos++ = tm.min; /* Minutes */
+ *pos++ = tm.sec; /* Seconds */
+ WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+ pos += 2;
+ *pos++ = 0; /* Reserved */
+
+ /* Time Error */
+ /* TODO: fill in an estimate on the error */
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+
+ *pos++ = hapd->time_update_counter++;
+
+ return 0;
+}
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+#ifdef CONFIG_WNM_AP
+ if (hapd->conf->ap_max_inactivity > 0) {
+ unsigned int val;
+ *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+ *pos++ = 3;
+ val = hapd->conf->ap_max_inactivity;
+ if (val > 68000)
+ val = 68000;
+ val *= 1000;
+ val /= 1024;
+ if (val == 0)
+ val = 1;
+ if (val > 65535)
+ val = 65535;
+ WPA_PUT_LE16(pos, val);
+ pos += 2;
+ *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+ }
+#endif /* CONFIG_WNM_AP */
+
+ return pos;
+}
+
+
+#ifdef CONFIG_MBO
+
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+ size_t len, int delta)
+{
+ u8 mbo[4];
+
+ mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
+ mbo[1] = 2;
+ /* Delta RSSI */
+ mbo[2] = delta;
+ /* Retry delay */
+ mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
+
+ return eid + mbo_add_ie(eid, len, mbo, 4);
+}
+
+
+u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+ u8 mbo[9], *mbo_pos = mbo;
+ u8 *pos = eid;
+
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+ return eid;
+
+ if (hapd->conf->mbo_enabled) {
+ *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+ *mbo_pos++ = 1;
+ /* Not Cellular aware */
+ *mbo_pos++ = 0;
+ }
+
+ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
+ *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = hapd->mbo_assoc_disallow;
+ }
+
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
+ u8 ctrl;
+
+ ctrl = OCE_RELEASE;
+ if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+ ctrl |= OCE_IS_STA_CFON;
+
+ *mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = ctrl;
+ }
+
+ pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
+
+ return pos;
+}
+
+
+u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
+{
+ u8 len;
+
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+ return 0;
+
+ /*
+ * MBO IE header (6) + Capability Indication attribute (3) +
+ * Association Disallowed attribute (3) = 12
+ */
+ len = 6;
+ if (hapd->conf->mbo_enabled)
+ len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+
+ /* OCE capability indication attribute (3) */
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
+ len += 3;
+
+ return len;
+}
+
+#endif /* CONFIG_MBO */
+
+
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+ return hapd->conf->owe_transition_ssid_len > 0 &&
+ !is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return 0;
+ return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+ return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+#ifdef CONFIG_OWE
+ u8 *pos = eid;
+ size_t elen;
+
+ if (hapd->conf->owe_transition_ifname[0] &&
+ !hostapd_eid_owe_trans_enabled(hapd))
+ hostapd_owe_trans_get_info(hapd);
+
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return pos;
+
+ elen = hostapd_eid_owe_trans_len(hapd);
+ if (len < elen) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Not enough room in the buffer for OWE IE");
+ return pos;
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = elen - 2;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = OWE_OUI_TYPE;
+ os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ *pos++ = hapd->conf->owe_transition_ssid_len;
+ os_memcpy(pos, hapd->conf->owe_transition_ssid,
+ hapd->conf->owe_transition_ssid_len);
+ pos += hapd->conf->owe_transition_ssid_len;
+
+ return pos;
+#else /* CONFIG_OWE */
+ return eid;
+#endif /* CONFIG_OWE */
+}
+
+
+size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_DPP2
+ if (hostapd_dpp_configurator_connectivity(hapd))
+ return 6;
+#endif /* CONFIG_DPP2 */
+ return 0;
+}
+
+
+u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+ u8 *pos = eid;
+
+#ifdef CONFIG_DPP2
+ if (!hostapd_dpp_configurator_connectivity(hapd) || len < 6)
+ return pos;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = DPP_CC_OUI_TYPE;
+#endif /* CONFIG_DPP2 */
+
+ return pos;
+}
+
+
+void ap_copy_sta_supp_op_classes(struct sta_info *sta,
+ const u8 *supp_op_classes,
+ size_t supp_op_classes_len)
+{
+ if (!supp_op_classes)
+ return;
+ os_free(sta->supp_op_classes);
+ sta->supp_op_classes = os_malloc(1 + supp_op_classes_len);
+ if (!sta->supp_op_classes)
+ return;
+
+ sta->supp_op_classes[0] = supp_op_classes_len;
+ os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
+ supp_op_classes_len);
+}
+
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_FILS
+ u8 *len;
+ u16 fils_info = 0;
+ size_t realms;
+ struct fils_realm *realm;
+
+ if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+ return pos;
+
+ realms = dl_list_len(&hapd->conf->fils_realms);
+ if (realms > 7)
+ realms = 7; /* 3 bit count field limits this to max 7 */
+
+ *pos++ = WLAN_EID_FILS_INDICATION;
+ len = pos++;
+ /* TODO: B0..B2: Number of Public Key Identifiers */
+ if (hapd->conf->erp_domain) {
+ /* B3..B5: Number of Realm Identifiers */
+ fils_info |= realms << 3;
+ }
+ /* TODO: B6: FILS IP Address Configuration */
+ if (hapd->conf->fils_cache_id_set)
+ fils_info |= BIT(7);
+ if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
+ fils_info |= BIT(8); /* HESSID Included */
+ /* FILS Shared Key Authentication without PFS Supported */
+ fils_info |= BIT(9);
+ if (hapd->conf->fils_dh_group) {
+ /* FILS Shared Key Authentication with PFS Supported */
+ fils_info |= BIT(10);
+ }
+ /* TODO: B11: FILS Public Key Authentication Supported */
+ /* B12..B15: Reserved */
+ WPA_PUT_LE16(pos, fils_info);
+ pos += 2;
+ if (hapd->conf->fils_cache_id_set) {
+ os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
+ pos += FILS_CACHE_ID_LEN;
+ }
+ if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
+ list) {
+ if (realms == 0)
+ break;
+ realms--;
+ os_memcpy(pos, realm->hash, 2);
+ pos += 2;
+ }
+ *len = pos - len - 1;
+#endif /* CONFIG_FILS */
+
+ return pos;
+}
+
+
+#ifdef CONFIG_OCV
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+ int ht_40mhz = 0;
+ int vht_80p80 = 0;
+ int requested_bw;
+
+ if (sta->ht_capabilities)
+ ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+
+ if (sta->vht_operation) {
+ struct ieee80211_vht_operation *oper = sta->vht_operation;
+
+ /*
+ * If a VHT Operation element was present, use it to determine
+ * the supported channel bandwidth.
+ */
+ if (oper->vht_op_info_chwidth == CHANWIDTH_USE_HT) {
+ requested_bw = ht_40mhz ? 40 : 20;
+ } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
+ requested_bw = 80;
+ } else {
+ int diff;
+
+ requested_bw = 160;
+ diff = abs((int)
+ oper->vht_op_info_chan_center_freq_seg0_idx -
+ (int)
+ oper->vht_op_info_chan_center_freq_seg1_idx);
+ vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
+ != 0 && diff > 16;
+ }
+ } else if (sta->vht_capabilities) {
+ struct ieee80211_vht_capabilities *capab;
+ int vht_chanwidth;
+
+ capab = sta->vht_capabilities;
+
+ /*
+ * If only the VHT Capabilities element is present (e.g., for
+ * normal clients), use it to determine the supported channel
+ * bandwidth.
+ */
+ vht_chanwidth = capab->vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ vht_80p80 = capab->vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+
+ /* TODO: Also take into account Extended NSS BW Support field */
+ requested_bw = vht_chanwidth ? 160 : 80;
+ } else {
+ requested_bw = ht_40mhz ? 40 : 20;
+ }
+
+ *bandwidth = requested_bw < ap_max_chanwidth ?
+ requested_bw : ap_max_chanwidth;
+
+ *seg1_idx = 0;
+ if (ap_seg1_idx && vht_80p80)
+ *seg1_idx = ap_seg1_idx;
+
+ return 0;
+}
+#endif /* CONFIG_OCV */
+
+
+u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+ u8 *pos = eid;
+ bool sae_pk = false;
+ u16 capab = 0;
+ size_t flen;
+
+ if (!(hapd->conf->wpa & WPA_PROTO_RSN))
+ return eid;
+
+#ifdef CONFIG_SAE_PK
+ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
+#endif /* CONFIG_SAE_PK */
+
+ if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ hapd->conf->sae_pwe == SAE_PWE_BOTH ||
+ hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
+ wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
+ hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) {
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+ if (sae_pk)
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+ }
+
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+
+ flen = (capab & 0xff00) ? 2 : 1;
+ if (len < 2 + flen || !capab)
+ return eid; /* no supported extended RSN capabilities */
+ capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = flen;
+ *pos++ = capab & 0x00ff;
+ capab >>= 8;
+ if (capab)
+ *pos++ = capab;
+
+ return pos;
+}
+
+
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+ /* check for QoS Map support */
+ if (ext_capab_ie_len >= 5) {
+ if (ext_capab_ie[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+
+ if (ext_capab_ie_len > 0) {
+ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+ os_free(sta->ext_capability);
+ sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+ if (sta->ext_capability) {
+ sta->ext_capability[0] = ext_capab_ie_len;
+ os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+ ext_capab_ie_len);
+ }
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_vht.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_vht.c
new file mode 100644
index 0000000..bd6462f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_vht.c
@@ -0,0 +1,384 @@
+/*
+ * hostapd / IEEE 802.11ac VHT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of BSD license
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+{
+ struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ u8 *pos = eid;
+ u8 chwidth;
+
+ if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+ mode->vht_capab == 0 && hapd->iface->hw_features) {
+ int i;
+
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ if (hapd->iface->hw_features[i].mode ==
+ HOSTAPD_MODE_IEEE80211A) {
+ mode = &hapd->iface->hw_features[i];
+ break;
+ }
+ }
+ }
+
+ *pos++ = WLAN_EID_VHT_CAP;
+ *pos++ = sizeof(*cap);
+
+ cap = (struct ieee80211_vht_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+ cap->vht_capabilities_info = host_to_le32(
+ hapd->iface->conf->vht_capab);
+
+ if (nsts != 0) {
+ u32 hapd_nsts;
+
+ hapd_nsts = le_to_host32(cap->vht_capabilities_info);
+ hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+ cap->vht_capabilities_info &=
+ ~(host_to_le32(hapd_nsts <<
+ VHT_CAP_BEAMFORMEE_STS_OFFSET));
+ cap->vht_capabilities_info |=
+ host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ }
+
+ chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+ if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
+ && ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
+ cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
+ cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
+ } else {
+ cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
+ }
+
+ /* Supported MCS set comes from hw */
+ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_vht_operation *oper;
+ le32 vht_capabilities_info;
+ u8 *pos = eid;
+ enum oper_chan_width oper_chwidth =
+ hostapd_get_oper_chwidth(hapd->iconf);
+ u8 seg0 = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+ u8 seg1 = hapd->iconf->vht_oper_centr_freq_seg1_idx;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ return eid;
+
+ *pos++ = WLAN_EID_VHT_OPERATION;
+ *pos++ = sizeof(*oper);
+
+ oper = (struct ieee80211_vht_operation *) pos;
+ os_memset(oper, 0, sizeof(*oper));
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->punct_bitmap) {
+ punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+ hapd->iconf->channel,
+ &oper_chwidth, &seg0, &seg1);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ /*
+ * center freq = 5 GHz + (5 * index)
+ * So index 42 gives center freq 5.210 GHz
+ * which is channel 42 in 5G band
+ */
+ oper->vht_op_info_chan_center_freq_seg0_idx = seg0;
+ oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
+
+ oper->vht_op_info_chwidth = oper_chwidth;
+ vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+ /*
+ * Convert 160 MHz channel width to new style as interop
+ * workaround.
+ */
+ oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
+ oper->vht_op_info_chan_center_freq_seg1_idx =
+ oper->vht_op_info_chan_center_freq_seg0_idx;
+ if (hapd->iconf->channel <
+ hapd->iconf->vht_oper_centr_freq_seg0_idx)
+ oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+ else
+ oper->vht_op_info_chan_center_freq_seg0_idx += 8;
+
+ if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
+ oper->vht_op_info_chan_center_freq_seg1_idx = 0;
+ } else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
+ /*
+ * Convert 80+80 MHz channel width to new style as interop
+ * workaround.
+ */
+ oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
+ }
+
+ /* VHT Basic MCS set comes from hw */
+ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+ oper->vht_basic_mcs_set = host_to_le16(0xfffc);
+ pos += sizeof(*oper);
+
+ return pos;
+}
+
+
+static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+ const u8 *sta_vht_capab)
+{
+ const struct ieee80211_vht_capabilities *vht_cap;
+ struct ieee80211_vht_capabilities ap_vht_cap;
+ u16 sta_rx_mcs_set, ap_tx_mcs_set;
+ int i;
+
+ if (!mode)
+ return 1;
+
+ /*
+ * Disable VHT caps for STAs for which there is not even a single
+ * allowed MCS in any supported number of streams, i.e., STA is
+ * advertising 3 (not supported) as VHT MCS rates for all supported
+ * stream cases.
+ */
+ os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
+ sizeof(ap_vht_cap.vht_supported_mcs_set));
+ vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
+
+ /* AP Tx MCS map vs. STA Rx MCS map */
+ sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
+ ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
+
+ for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
+ if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+ continue;
+
+ if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+ continue;
+
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "No matching VHT MCS found between AP TX and STA RX");
+ return 0;
+}
+
+
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab)
+{
+ /* Disable VHT caps for STAs associated to no-VHT BSSes. */
+ if (!vht_capab || !(sta->flags & WLAN_STA_WMM) ||
+ !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
+ !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
+ sta->flags &= ~WLAN_STA_VHT;
+ os_free(sta->vht_capabilities);
+ sta->vht_capabilities = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (sta->vht_capabilities == NULL) {
+ sta->vht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+ if (sta->vht_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_VHT;
+ os_memcpy(sta->vht_capabilities, vht_capab,
+ sizeof(struct ieee80211_vht_capabilities));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper)
+{
+ if (!vht_oper) {
+ os_free(sta->vht_operation);
+ sta->vht_operation = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->vht_operation) {
+ sta->vht_operation =
+ os_zalloc(sizeof(struct ieee80211_vht_operation));
+ if (!sta->vht_operation)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memcpy(sta->vht_operation, vht_oper,
+ sizeof(struct ieee80211_vht_operation));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len)
+{
+ const u8 *vht_capab;
+ unsigned int vht_capab_len;
+
+ if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+ hapd->conf->disable_11ac)
+ goto no_capab;
+
+ /* The VHT Capabilities element embedded in vendor VHT */
+ vht_capab = ie + 5;
+ if (vht_capab[0] != WLAN_EID_VHT_CAP)
+ goto no_capab;
+ vht_capab_len = vht_capab[1];
+ if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+ (int) vht_capab_len > ie + len - vht_capab - 2)
+ goto no_capab;
+ vht_capab += 2;
+
+ if (sta->vht_capabilities == NULL) {
+ sta->vht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+ if (sta->vht_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+ os_memcpy(sta->vht_capabilities, vht_capab,
+ sizeof(struct ieee80211_vht_capabilities));
+ return WLAN_STATUS_SUCCESS;
+
+no_capab:
+ sta->flags &= ~WLAN_STA_VENDOR_VHT;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+ /* Vendor VHT is applicable only to 2.4 GHz */
+ if (!hapd->iface->current_mode ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return eid;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = (5 + /* The Vendor OUI, type and subtype */
+ 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation));
+
+ WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+ pos += 4;
+ *pos++ = VENDOR_VHT_SUBTYPE;
+ pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+
+ return pos;
+}
+
+
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper_notif)
+{
+ if (!vht_oper_notif) {
+ sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
+ sta->vht_opmode = *vht_oper_notif;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+ struct ieee80211_vht_capabilities *vht_cap,
+ struct ieee80211_vht_capabilities *neg_vht_cap)
+{
+ u32 cap, own_cap, sym_caps;
+
+ if (vht_cap == NULL)
+ return;
+ os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
+
+ cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
+ own_cap = hapd->iconf->vht_capab;
+
+ /* mask out symmetric VHT capabilities we don't support */
+ sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
+ cap &= ~sym_caps | (own_cap & sym_caps);
+
+ /* mask out beamformer/beamformee caps if not supported */
+ if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+ VHT_CAP_BEAMFORMEE_STS_MAX);
+
+ if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
+ VHT_CAP_SOUNDING_DIMENSION_MAX);
+
+ if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+ cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+ if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
+ cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /* mask channel widths we don't support */
+ switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+ case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+ break;
+ case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+ if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+ cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+ break;
+ default:
+ cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ break;
+ }
+
+ if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
+ cap &= ~VHT_CAP_SHORT_GI_160;
+
+ /*
+ * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+ * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+ */
+ if (!(own_cap & VHT_CAP_RXSTBC_MASK))
+ cap &= ~VHT_CAP_TXSTBC;
+ if (!(own_cap & VHT_CAP_TXSTBC))
+ cap &= ~VHT_CAP_RXSTBC_MASK;
+
+ neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.c
new file mode 100644
index 0000000..46b120d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.c
@@ -0,0 +1,3136 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "pmksa_cache_auth.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wps_hostapd.h"
+#include "hs20.h"
+/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
+#include "ieee802_11.h"
+#include "ieee802_1x.h"
+#include "wpa_auth_kay.h"
+
+
+#ifdef CONFIG_HS20
+static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_HS20 */
+static bool ieee802_1x_finished(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ int remediation, bool logoff);
+
+
+static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 type, const u8 *data, size_t datalen)
+{
+ u8 *buf;
+ struct ieee802_1x_hdr *xhdr;
+ size_t len;
+ int encrypt = 0;
+
+ len = sizeof(*xhdr) + datalen;
+ buf = os_zalloc(len);
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)",
+ __func__, (unsigned long) len);
+ return;
+ }
+
+ xhdr = (struct ieee802_1x_hdr *) buf;
+ xhdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+ if (xhdr->version > 2 && hapd->conf->macsec_policy == 0)
+ xhdr->version = 2;
+#endif /* CONFIG_MACSEC */
+ xhdr->type = type;
+ xhdr->length = host_to_be16(datalen);
+
+ if (datalen > 0 && data != NULL)
+ os_memcpy(xhdr + 1, data, datalen);
+
+ if (wpa_auth_pairwise_set(sta->wpa_sm))
+ encrypt = 1;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_eapol_frame_io) {
+ size_t hex_len = 2 * len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "EAPOL-TX " MACSTR " %s",
+ MAC2STR(sta->addr), hex);
+ os_free(hex);
+ }
+ } else
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (sta->flags & WLAN_STA_PREAUTH) {
+ rsn_preauth_send(hapd, sta, buf, len);
+ } else {
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
+#endif /* CONFIG_IEEE80211BE */
+ hostapd_drv_hapd_send_eapol(
+ hapd, sta->addr, buf, len,
+ encrypt, hostapd_sta_flags_to_drv(sta->flags), link_id);
+ }
+
+ os_free(buf);
+}
+
+
+static void ieee802_1x_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ bool authorized, bool mld)
+{
+ int res;
+
+ if (sta->flags & WLAN_STA_PREAUTH)
+ return;
+
+ ap_sta_set_authorized(hapd, sta, authorized);
+ res = hostapd_set_authorized(hapd, sta, authorized);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
+ authorized ? "" : "un");
+
+ if (!mld && res && errno != ENOENT) {
+ wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+ " flags for kernel driver (errno=%d).",
+ MAC2STR(sta->addr), errno);
+ } else if (mld && res) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Could not set station " MACSTR " flags",
+ MAC2STR(sta->addr));
+ }
+
+ if (authorized) {
+ os_get_reltime(&sta->connected_time);
+ accounting_sta_start(hapd, sta);
+ }
+}
+
+
+static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ bool authorized)
+{
+#ifdef CONFIG_IEEE80211BE
+ unsigned int i, link_id;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return;
+
+ /*
+ * Authorizing the station should be done only in the station
+ * performing the association
+ */
+ if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
+ return;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &sta->mld_info.links[link_id];
+
+ if (!link->valid)
+ continue;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+ struct hostapd_data *tmp_hapd =
+ hapd->iface->interfaces->iface[i]->bss[0];
+
+ if (tmp_hapd->conf->mld_ap ||
+ hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ tmp_sta->mld_assoc_link_id !=
+ sta->mld_assoc_link_id ||
+ tmp_sta->aid != sta->aid)
+ continue;
+
+ ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
+ authorized, true);
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ ieee802_1x_set_authorized(hapd, sta, authorized, false);
+ ieee802_1x_ml_set_sta_authorized(hapd, sta, !!authorized);
+}
+
+
+#ifdef CONFIG_WEP
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+
+static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ int idx, int broadcast,
+ u8 *key_data, size_t key_len)
+{
+ u8 *buf, *ekey;
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ size_t len, ekey_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ len = sizeof(*key) + key_len;
+ buf = os_zalloc(sizeof(*hdr) + len);
+ if (!buf)
+ return;
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ key->type = EAPOL_KEY_TYPE_RC4;
+ WPA_PUT_BE16(key->key_length, key_len);
+ wpa_get_ntp_timestamp(key->replay_counter);
+ if (os_memcmp(key->replay_counter,
+ hapd->last_1x_eapol_key_replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN) <= 0) {
+ /* NTP timestamp did not increment from last EAPOL-Key frame;
+ * use previously used value + 1 instead. */
+ inc_byte_array(hapd->last_1x_eapol_key_replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+ os_memcpy(key->replay_counter,
+ hapd->last_1x_eapol_key_replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+ } else {
+ os_memcpy(hapd->last_1x_eapol_key_replay_counter,
+ key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+ }
+
+ if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
+ wpa_printf(MSG_ERROR, "Could not get random numbers");
+ os_free(buf);
+ return;
+ }
+
+ key->key_index = idx | (broadcast ? 0 : BIT(7));
+ if (hapd->conf->eapol_key_index_workaround) {
+ /* According to some information, WinXP Supplicant seems to
+ * interpret bit7 as an indication whether the key is to be
+ * activated, so make it possible to enable workaround that
+ * sets this bit for all keys. */
+ key->key_index |= BIT(7);
+ }
+
+ /* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
+ * MSK[32..63] is used to sign the message. */
+ if (!sm->eap_if->eapKeyData || sm->eap_if->eapKeyDataLen < 64) {
+ wpa_printf(MSG_ERROR,
+ "No eapKeyData available for encrypting and signing EAPOL-Key");
+ os_free(buf);
+ return;
+ }
+ os_memcpy((u8 *) (key + 1), key_data, key_len);
+ ekey_len = sizeof(key->key_iv) + 32;
+ ekey = os_malloc(ekey_len);
+ if (!ekey) {
+ wpa_printf(MSG_ERROR, "Could not encrypt key");
+ os_free(buf);
+ return;
+ }
+ os_memcpy(ekey, key->key_iv, sizeof(key->key_iv));
+ os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32);
+ rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len);
+ os_free(ekey);
+
+ /* This header is needed here for HMAC-MD5, but it will be regenerated
+ * in ieee802_1x_send() */
+ hdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+ if (hdr->version > 2)
+ hdr->version = 2;
+#endif /* CONFIG_MACSEC */
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = host_to_be16(len);
+ hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
+ key->key_signature);
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
+ " (%s index=%d)", MAC2STR(sm->addr),
+ broadcast ? "broadcast" : "unicast", idx);
+ ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapolFramesTx++;
+ os_free(buf);
+}
+
+
+static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_authenticator *eapol = hapd->eapol_auth;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm || !sm->eap_if->eapKeyData)
+ return;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
+ MAC2STR(sta->addr));
+
+#ifndef CONFIG_NO_VLAN
+ if (sta->vlan_id > 0) {
+ wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
+ return;
+ }
+#endif /* CONFIG_NO_VLAN */
+
+ if (eapol->default_wep_key) {
+ ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len);
+ }
+
+ if (hapd->conf->individual_wep_key_len > 0) {
+ u8 *ikey;
+
+ ikey = os_malloc(hapd->conf->individual_wep_key_len);
+ if (!ikey ||
+ random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+ {
+ wpa_printf(MSG_ERROR,
+ "Could not generate random individual WEP key");
+ os_free(ikey);
+ return;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "Individual WEP key",
+ ikey, hapd->conf->individual_wep_key_len);
+
+ ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
+ hapd->conf->individual_wep_key_len);
+
+ /* TODO: set encryption in TX callback, i.e., only after STA
+ * has ACKed EAPOL-Key frame */
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+ sta->addr, 0, 0, 1, NULL, 0, ikey,
+ hapd->conf->individual_wep_key_len,
+ KEY_FLAG_PAIRWISE_RX_TX)) {
+ wpa_printf(MSG_ERROR,
+ "Could not set individual WEP encryption");
+ }
+
+ os_free(ikey);
+ }
+}
+
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+#endif /* CONFIG_WEP */
+
+
+const char *radius_mode_txt(struct hostapd_data *hapd)
+{
+ switch (hapd->iface->conf->hw_mode) {
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "802.11ad";
+ case HOSTAPD_MODE_IEEE80211A:
+ return "802.11a";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "802.11g";
+ case HOSTAPD_MODE_IEEE80211B:
+ default:
+ return "802.11b";
+ }
+}
+
+
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int i;
+ u8 rate = 0;
+
+ for (i = 0; i < sta->supported_rates_len; i++)
+ if ((sta->supported_rates[i] & 0x7f) > rate)
+ rate = sta->supported_rates[i] & 0x7f;
+
+ return rate;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
+ struct eapol_state_machine *sm,
+ const u8 *eap, size_t len)
+{
+ const u8 *identity;
+ size_t identity_len;
+ const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
+
+ if (len <= sizeof(struct eap_hdr) ||
+ (hdr->code == EAP_CODE_RESPONSE &&
+ eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
+ (hdr->code == EAP_CODE_INITIATE &&
+ eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
+ (hdr->code != EAP_CODE_RESPONSE &&
+ hdr->code != EAP_CODE_INITIATE))
+ return;
+
+ eap_erp_update_identity(sm->eap, eap, len);
+ identity = eap_get_identity(sm->eap, &identity_len);
+ if (!identity)
+ return;
+
+ /* Save station identity for future RADIUS packets */
+ os_free(sm->identity);
+ sm->identity = (u8 *) dup_binstr(identity, identity_len);
+ if (!sm->identity) {
+ sm->identity_len = 0;
+ return;
+ }
+
+ sm->identity_len = identity_len;
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
+ sm->dot1xAuthEapolRespIdFramesRx++;
+}
+
+
+static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u32 suite;
+ int ver, val;
+
+ ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
+ val = wpa_auth_get_pairwise(sta->wpa_sm);
+ suite = wpa_cipher_to_suite(ver, val);
+ if (val != -1 &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
+ return -1;
+ }
+
+ suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
+ hapd->conf->osen) ?
+ WPA_PROTO_RSN : WPA_PROTO_WPA,
+ hapd->conf->wpa_group);
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
+ return -1;
+ }
+
+ val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ suite = wpa_akm_to_suite(val);
+ if (val != -1 &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_AKM_SUITE) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
+ return -1;
+ }
+
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ hapd->conf->group_mgmt_cipher);
+ if (!hostapd_config_get_radius_attr(
+ req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
+ wpa_printf(MSG_ERROR,
+ "Could not add WLAN-Group-Mgmt-Cipher");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int add_common_radius_sta_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ char buf[128];
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_SERVICE_TYPE) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+ RADIUS_SERVICE_TYPE_FRAMED)) {
+ wpa_printf(MSG_ERROR, "Could not add Service-Type");
+ return -1;
+ }
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_PORT) &&
+ sta->aid > 0 &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Port");
+ return -1;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
+ return -1;
+ }
+
+ if (sta->flags & WLAN_STA_PREAUTH) {
+ os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+ sizeof(buf));
+ } else {
+ os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+ radius_sta_rate(hapd, sta) / 2,
+ (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+ radius_mode_txt(hapd));
+ buf[sizeof(buf) - 1] = '\0';
+ }
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_CONNECT_INFO) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Connect-Info");
+ return -1;
+ }
+
+ if (sta->acct_session_id) {
+ os_snprintf(buf, sizeof(buf), "%016llX",
+ (unsigned long long) sta->acct_session_id);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+ return -1;
+ }
+ }
+
+ if ((hapd->conf->wpa & 2) &&
+ !hapd->conf->disable_pmksa_caching &&
+ sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) {
+ os_snprintf(buf, sizeof(buf), "%016llX",
+ (unsigned long long)
+ sta->eapol_sm->acct_multi_session_id);
+ if (!radius_msg_add_attr(
+ msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_INFO,
+ "Could not add Acct-Multi-Session-Id");
+ return -1;
+ }
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+ sta->wpa_sm &&
+ (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+ sta->auth_alg == WLAN_AUTH_FT) &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
+ WPA_GET_BE16(
+ hapd->conf->mobility_domain))) {
+ wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
+ add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ char buf[128];
+ struct hostapd_radius_attr *attr;
+ int len;
+
+ if (hapd->conf->dynamic_own_ip_addr)
+ radius_client_get_local_addr(hapd->radius,
+ &hapd->conf->own_ip_addr);
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IP_ADDRESS) &&
+ hapd->conf->own_ip_addr.af == AF_INET &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
+ return -1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+ hapd->conf->own_ip_addr.af == AF_INET6 &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
+ return -1;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IDENTIFIER) &&
+ hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ os_strlen(hapd->conf->nas_identifier))) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
+ return -1;
+ }
+
+ len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":",
+ MAC2STR(hapd->own_addr));
+ os_memcpy(&buf[len], hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+ len += hapd->conf->ssid.ssid_len;
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_CALLED_STATION_ID) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, len)) {
+ wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
+ return -1;
+ }
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_PORT_TYPE) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+ RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
+ return -1;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ if (hapd->conf->interworking &&
+ !is_zero_ether_addr(hapd->conf->hessid)) {
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(hapd->conf->hessid));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_HESSID) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
+ return -1;
+ }
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
+ return -1;
+
+ for (attr = req_attr; attr; attr = attr->next) {
+ if (!radius_msg_add_attr(msg, attr->type,
+ wpabuf_head(attr->val),
+ wpabuf_len(attr->val))) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
+ struct radius_msg *msg, int acct)
+{
+#ifdef CONFIG_SQLITE
+ const char *attrtxt;
+ char addrtxt[3 * ETH_ALEN];
+ char *sql;
+ sqlite3_stmt *stmt = NULL;
+
+ if (!hapd->rad_attr_db)
+ return 0;
+
+ os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(sta->addr));
+
+ sql = "SELECT attr FROM radius_attributes WHERE sta=? AND (reqtype=? OR reqtype IS NULL);";
+ if (sqlite3_prepare_v2(hapd->rad_attr_db, sql, os_strlen(sql), &stmt,
+ NULL) != SQLITE_OK) {
+ wpa_printf(MSG_ERROR, "DB: Failed to prepare SQL statement: %s",
+ sqlite3_errmsg(hapd->rad_attr_db));
+ return -1;
+ }
+ sqlite3_bind_text(stmt, 1, addrtxt, os_strlen(addrtxt), SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 2, acct ? "acct" : "auth", 4, SQLITE_STATIC);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ struct hostapd_radius_attr *attr;
+ struct radius_attr_hdr *hdr;
+
+ attrtxt = (const char *) sqlite3_column_text(stmt, 0);
+ attr = hostapd_parse_radius_attr(attrtxt);
+ if (!attr) {
+ wpa_printf(MSG_ERROR,
+ "Skipping invalid attribute from SQL: %s",
+ attrtxt);
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "Adding RADIUS attribute from SQL: %s",
+ attrtxt);
+ hdr = radius_msg_add_attr(msg, attr->type,
+ wpabuf_head(attr->val),
+ wpabuf_len(attr->val));
+ hostapd_config_free_radius_attr(attr);
+ if (!hdr) {
+ wpa_printf(MSG_ERROR,
+ "Could not add RADIUS attribute from SQL");
+ continue;
+ }
+ }
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+ sqlite3_finalize(stmt);
+#endif /* CONFIG_SQLITE */
+
+ return 0;
+}
+
+
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len)
+{
+ struct radius_msg *msg;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ ieee802_1x_learn_identity(hapd, sm, eap, len);
+
+ wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS packet");
+
+ sm->radius_identifier = radius_client_get_id(hapd->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+ sm->radius_identifier);
+ if (!msg) {
+ wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
+ return;
+ }
+
+ if (radius_msg_make_authenticator(msg) < 0) {
+ wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+ goto fail;
+ }
+
+ if (sm->identity &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+ sm->identity, sm->identity_len)) {
+ wpa_printf(MSG_INFO, "Could not add User-Name");
+ goto fail;
+ }
+
+ if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
+ msg) < 0)
+ goto fail;
+
+ if (sta && add_sqlite_radius_attr(hapd, sta, msg, 0) < 0)
+ goto fail;
+
+ /* TODO: should probably check MTU from driver config; 2304 is max for
+ * IEEE 802.11, but use 1400 to avoid problems with too large packets
+ */
+ if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+ RADIUS_ATTR_FRAMED_MTU) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+ wpa_printf(MSG_INFO, "Could not add Framed-MTU");
+ goto fail;
+ }
+
+ if (!radius_msg_add_eap(msg, eap, len)) {
+ wpa_printf(MSG_INFO, "Could not add EAP-Message");
+ goto fail;
+ }
+
+ /* State attribute must be copied if and only if this packet is
+ * Access-Request reply to the previous Access-Challenge */
+ if (sm->last_recv_radius &&
+ radius_msg_get_hdr(sm->last_recv_radius)->code ==
+ RADIUS_CODE_ACCESS_CHALLENGE) {
+ int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
+ RADIUS_ATTR_STATE);
+ if (res < 0) {
+ wpa_printf(MSG_INFO,
+ "Could not copy State attribute from previous Access-Challenge");
+ goto fail;
+ }
+ if (res > 0)
+ wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
+ }
+
+ if (hapd->conf->radius_request_cui) {
+ const u8 *cui;
+ size_t cui_len;
+ /* Add previously learned CUI or nul CUI to request CUI */
+ if (sm->radius_cui) {
+ cui = wpabuf_head(sm->radius_cui);
+ cui_len = wpabuf_len(sm->radius_cui);
+ } else {
+ cui = (const u8 *) "\0";
+ cui_len = 1;
+ }
+ if (!radius_msg_add_attr(msg,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ cui, cui_len)) {
+ wpa_printf(MSG_ERROR, "Could not add CUI");
+ goto fail;
+ }
+ }
+
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20) {
+ u8 ver = hapd->conf->hs20_release - 1;
+
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
+ &ver, 1)) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 AP version");
+ goto fail;
+ }
+
+ if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
+ const u8 *pos;
+ u8 buf[3];
+ u16 id;
+
+ pos = wpabuf_head_u8(sta->hs20_ie);
+ buf[0] = (*pos) >> 4;
+ if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
+ wpabuf_len(sta->hs20_ie) >= 3)
+ id = WPA_GET_LE16(pos + 1);
+ else
+ id = 0;
+ WPA_PUT_BE16(buf + 1, id);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
+ buf, sizeof(buf))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 STA version");
+ goto fail;
+ }
+ }
+
+ if (sta->roaming_consortium &&
+ !radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
+ wpabuf_head(sta->roaming_consortium),
+ wpabuf_len(sta->roaming_consortium))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Roaming Consortium");
+ goto fail;
+ }
+
+ if (hapd->conf->t_c_filename) {
+ be32 timestamp;
+
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
+ (const u8 *) hapd->conf->t_c_filename,
+ os_strlen(hapd->conf->t_c_filename))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 T&C Filename");
+ goto fail;
+ }
+
+ timestamp = host_to_be32(hapd->conf->t_c_timestamp);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
+ (const u8 *) ×tamp,
+ sizeof(timestamp))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Timestamp");
+ goto fail;
+ }
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
+ goto fail;
+
+ return;
+
+ fail:
+ radius_msg_free(msg);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void handle_eap_response(struct hostapd_data *hapd,
+ struct sta_info *sta, struct eap_hdr *eap,
+ size_t len)
+{
+ u8 type, *data;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ data = (u8 *) (eap + 1);
+
+ if (len < sizeof(*eap) + 1) {
+ wpa_printf(MSG_INFO, "%s: too short response data", __func__);
+ return;
+ }
+
+ sm->eap_type_supp = type = data[0];
+
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+ "id=%d len=%d) from STA: EAP Response-%s (%d)",
+ eap->code, eap->identifier, be_to_host16(eap->length),
+ eap_server_get_name(0, type), type);
+
+ sm->dot1xAuthEapolRespFramesRx++;
+
+ wpabuf_free(sm->eap_if->eapRespData);
+ sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+ sm->eapolEap = true;
+}
+
+
+static void handle_eap_initiate(struct hostapd_data *hapd,
+ struct sta_info *sta, struct eap_hdr *eap,
+ size_t len)
+{
+#ifdef CONFIG_ERP
+ u8 type, *data;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ if (len < sizeof(*eap) + 1) {
+ wpa_printf(MSG_INFO, "%s: too short response data", __func__);
+ return;
+ }
+
+ data = (u8 *) (eap + 1);
+ type = data[0];
+
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "received EAP packet (code=%d id=%d len=%d) from STA: EAP Initiate type %u",
+ eap->code, eap->identifier, be_to_host16(eap->length),
+ type);
+
+ wpabuf_free(sm->eap_if->eapRespData);
+ sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+ sm->eapolEap = true;
+#endif /* CONFIG_ERP */
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_code_str(u8 code)
+{
+ switch (code) {
+ case EAP_CODE_REQUEST:
+ return "request";
+ case EAP_CODE_RESPONSE:
+ return "response";
+ case EAP_CODE_SUCCESS:
+ return "success";
+ case EAP_CODE_FAILURE:
+ return "failure";
+ case EAP_CODE_INITIATE:
+ return "initiate";
+ case EAP_CODE_FINISH:
+ return "finish";
+ default:
+ return "unknown";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+/* Process incoming EAP packet from Supplicant */
+static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+ struct eap_hdr *eap;
+ u16 eap_len;
+
+ if (len < sizeof(*eap)) {
+ wpa_printf(MSG_INFO, " too short EAP packet");
+ return;
+ }
+
+ eap = (struct eap_hdr *) buf;
+
+ eap_len = be_to_host16(eap->length);
+ wpa_printf(MSG_DEBUG, "EAP: code=%d (%s) identifier=%d length=%d",
+ eap->code, eap_code_str(eap->code), eap->identifier,
+ eap_len);
+ if (eap_len < sizeof(*eap)) {
+ wpa_printf(MSG_DEBUG, " Invalid EAP length");
+ return;
+ } else if (eap_len > len) {
+ wpa_printf(MSG_DEBUG,
+ " Too short frame to contain this EAP packet");
+ return;
+ } else if (eap_len < len) {
+ wpa_printf(MSG_DEBUG,
+ " Ignoring %lu extra bytes after EAP packet",
+ (unsigned long) len - eap_len);
+ }
+
+ switch (eap->code) {
+ case EAP_CODE_RESPONSE:
+ handle_eap_response(hapd, sta, eap, eap_len);
+ break;
+ case EAP_CODE_INITIATE:
+ handle_eap_initiate(hapd, sta, eap, eap_len);
+ break;
+ }
+}
+
+
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int flags = 0;
+
+ if (sta->flags & WLAN_STA_PREAUTH)
+ flags |= EAPOL_SM_PREAUTH;
+ if (sta->wpa_sm) {
+ flags |= EAPOL_SM_USES_WPA;
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
+ flags |= EAPOL_SM_FROM_PMKSA_CACHE;
+ }
+ return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
+ sta->wps_ie, sta->p2p_ie, sta,
+ sta->identity, sta->radius_cui);
+}
+
+
+static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf,
+ size_t len, enum frame_encryption encrypted)
+{
+ if (sta->pending_eapol_rx) {
+ wpabuf_free(sta->pending_eapol_rx->buf);
+ } else {
+ sta->pending_eapol_rx =
+ os_malloc(sizeof(*sta->pending_eapol_rx));
+ if (!sta->pending_eapol_rx)
+ return;
+ }
+
+ sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len);
+ if (!sta->pending_eapol_rx->buf) {
+ os_free(sta->pending_eapol_rx);
+ sta->pending_eapol_rx = NULL;
+ return;
+ }
+
+ sta->pending_eapol_rx->encrypted = encrypted;
+ os_get_reltime(&sta->pending_eapol_rx->rx_time);
+}
+
+
+static bool ieee802_1x_check_encryption(struct sta_info *sta,
+ enum frame_encryption encrypted,
+ u8 type)
+{
+ if (encrypted != FRAME_NOT_ENCRYPTED)
+ return true;
+ if (type != IEEE802_1X_TYPE_EAP_PACKET &&
+ type != IEEE802_1X_TYPE_EAPOL_START &&
+ type != IEEE802_1X_TYPE_EAPOL_LOGOFF)
+ return true;
+ if (!(sta->flags & WLAN_STA_MFP))
+ return true;
+ return !wpa_auth_pairwise_set(sta->wpa_sm);
+}
+
+
+/**
+ * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
+ * @hapd: hostapd BSS data
+ * @sa: Source address (sender of the EAPOL frame)
+ * @buf: EAPOL frame
+ * @len: Length of buf in octets
+ * @encrypted: Whether the frame was encrypted
+ *
+ * This function is called for each incoming EAPOL frame from the interface
+ */
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+ size_t len, enum frame_encryption encrypted)
+{
+ struct sta_info *sta;
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ u16 datalen;
+ struct rsn_pmksa_cache_entry *pmksa;
+ int key_mgmt;
+
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
+ !hapd->conf->wps_state)
+ return;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR
+ " (encrypted=%d)",
+ (unsigned long) len, MAC2STR(sa), encrypted);
+ sta = ap_get_sta(hapd, sa);
+ if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X data frame from not associated/Pre-authenticating STA");
+
+ if (sta && (sta->flags & WLAN_STA_AUTH)) {
+ wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
+ " for later use", MAC2STR(sta->addr));
+ ieee802_1x_save_eapol(sta, buf, len, encrypted);
+ }
+
+ return;
+ }
+
+ if (len < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, " too short IEEE 802.1X packet");
+ return;
+ }
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ datalen = be_to_host16(hdr->length);
+ wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d",
+ hdr->version, hdr->type, datalen);
+
+ if (len - sizeof(*hdr) < datalen) {
+ wpa_printf(MSG_INFO,
+ " frame too short for this IEEE 802.1X packet");
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
+ return;
+ }
+ if (len - sizeof(*hdr) > datalen) {
+ wpa_printf(MSG_DEBUG,
+ " ignoring %lu extra octets after IEEE 802.1X packet",
+ (unsigned long) len - sizeof(*hdr) - datalen);
+ }
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
+ sta->eapol_sm->dot1xAuthEapolFramesRx++;
+ }
+
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
+ hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+ (key->type == EAPOL_KEY_TYPE_WPA ||
+ key->type == EAPOL_KEY_TYPE_RSN)) {
+ wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
+ sizeof(*hdr) + datalen);
+ return;
+ }
+
+ if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
+ return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (key_mgmt != -1 &&
+ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+ key_mgmt == WPA_KEY_MGMT_DPP)) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Ignore EAPOL message - STA is using PSK");
+ return;
+ }
+
+ if (!ieee802_1x_check_encryption(sta, encrypted, hdr->type)) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Discard unencrypted EAPOL message - encryption was expected");
+ return;
+ }
+
+ if (!sta->eapol_sm) {
+ sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+ if (!sta->eapol_sm)
+ return;
+
+#ifdef CONFIG_WPS
+ if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
+ u32 wflags = sta->flags & (WLAN_STA_WPS |
+ WLAN_STA_WPS2 |
+ WLAN_STA_MAYBE_WPS);
+ if (wflags == WLAN_STA_MAYBE_WPS ||
+ wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a
+ * possible WPS STA initiates the handshake
+ * with EAPOL-Start. Only allow the wait to be
+ * skipped if the STA is known to support WPS
+ * 2.0.
+ */
+ wpa_printf(MSG_DEBUG,
+ "WPS: Do not start EAPOL until EAPOL-Start is received");
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
+ }
+#endif /* CONFIG_WPS */
+
+ sta->eapol_sm->eap_if->portEnabled = true;
+ }
+
+ /* since we support version 1, we can ignore version field and proceed
+ * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
+ /* TODO: actually, we are not version 1 anymore.. However, Version 2
+ * does not change frame contents, so should be ok to process frames
+ * more or less identically. Some changes might be needed for
+ * verification of fields. */
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_START:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "received EAPOL-Start from STA");
+ sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "cached PMKSA available - ignore it since STA sent EAPOL-Start");
+ wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
+ }
+ sta->eapol_sm->eapolStart = true;
+ sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+ eap_server_clear_identity(sta->eapol_sm->eap);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "received EAPOL-Logoff from STA");
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ accounting_sta_stop(hapd, sta);
+ sta->eapol_sm->eapolLogoff = true;
+ sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+ eap_server_clear_identity(sta->eapol_sm->eap);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_KEY:
+ wpa_printf(MSG_DEBUG, " EAPOL-Key");
+ if (!ap_sta_is_authorized(sta)) {
+ wpa_printf(MSG_DEBUG,
+ " Dropped key data from unauthorized Supplicant");
+ break;
+ }
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+ wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert");
+ /* TODO: implement support for this; show data */
+ break;
+
+#ifdef CONFIG_MACSEC
+ case IEEE802_1X_TYPE_EAPOL_MKA:
+ wpa_printf(MSG_EXCESSIVE,
+ "EAPOL type %d will be handled by MKA", hdr->type);
+ break;
+#endif /* CONFIG_MACSEC */
+
+ default:
+ wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type");
+ sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
+ break;
+ }
+
+ eapol_auth_step(sta->eapol_sm);
+}
+
+
+/**
+ * ieee802_1x_new_station - Start IEEE 802.1X authentication
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ *
+ * This function is called to start IEEE 802.1X authentication when a new
+ * station completes IEEE 802.11 association.
+ */
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct rsn_pmksa_cache_entry *pmksa;
+ int reassoc = 1;
+ int force_1x = 0;
+ int key_mgmt;
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state &&
+ ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
+ (sta->flags & WLAN_STA_WPS))) {
+ /*
+ * Need to enable IEEE 802.1X/EAPOL state machines for possible
+ * WPS handshake even if IEEE 802.1X/EAPOL is not used for
+ * authentication in this BSS.
+ */
+ force_1x = 1;
+ }
+#endif /* CONFIG_WPS */
+
+ if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPS to PSK.
+ */
+ ieee802_1x_free_station(hapd, sta);
+ return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (key_mgmt != -1 &&
+ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+ key_mgmt == WPA_KEY_MGMT_DPP)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPA-EAP to PSK.
+ */
+ ieee802_1x_free_station(hapd, sta);
+ return;
+ }
+
+ if (!sta->eapol_sm) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "start authentication");
+ sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+ if (!sta->eapol_sm) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "failed to allocate state machine");
+ return;
+ }
+ reassoc = 0;
+ }
+
+#ifdef CONFIG_WPS
+ sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+ if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
+ !(sta->flags & WLAN_STA_WPS2)) {
+ /*
+ * Delay EAPOL frame transmission until a possible WPS STA
+ * initiates the handshake with EAPOL-Start. Only allow the
+ * wait to be skipped if the STA is known to support WPS 2.0.
+ */
+ wpa_printf(MSG_DEBUG,
+ "WPS: Do not start EAPOL until EAPOL-Start is received");
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
+#endif /* CONFIG_WPS */
+
+ sta->eapol_sm->eap_if->portEnabled = true;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from FT - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing FT information from R0KH. */
+ sta->eapol_sm->keyRun = true;
+ sta->eapol_sm->eap_if->eapKeyAvailable = true;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = true;
+ sta->eapol_sm->authFail = false;
+ sta->eapol_sm->portValid = true;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ ap_sta_bind_vlan(hapd, sta);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from FILS - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing FILS information. */
+ sta->eapol_sm->keyRun = true;
+ sta->eapol_sm->eap_if->eapKeyAvailable = true;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = true;
+ sta->eapol_sm->authFail = false;
+ sta->eapol_sm->portValid = true;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing PMKSA information in the cache. */
+ sta->eapol_sm->keyRun = true;
+ sta->eapol_sm->eap_if->eapKeyAvailable = true;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = true;
+ sta->eapol_sm->authFail = false;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm);
+ ap_sta_bind_vlan(hapd, sta);
+ } else {
+ if (reassoc) {
+ /*
+ * Force EAPOL state machines to start
+ * re-authentication without having to wait for the
+ * Supplicant to send EAPOL-Start.
+ */
+ sta->eapol_sm->reAuthenticate = true;
+ }
+ eapol_auth_step(sta->eapol_sm);
+ }
+}
+
+
+void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+#ifdef CONFIG_HS20
+ eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
+#endif /* CONFIG_HS20 */
+
+ if (sta->pending_eapol_rx) {
+ wpabuf_free(sta->pending_eapol_rx->buf);
+ os_free(sta->pending_eapol_rx);
+ sta->pending_eapol_rx = NULL;
+ }
+
+ if (!sm)
+ return;
+
+ sta->eapol_sm = NULL;
+
+#ifndef CONFIG_NO_RADIUS
+ radius_msg_free(sm->last_recv_radius);
+ radius_free_class(&sm->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+ eapol_auth_free(sm);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct wpabuf *eap;
+ const struct eap_hdr *hdr;
+ int eap_type = -1;
+ char buf[64];
+ struct radius_msg *msg;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm || !sm->last_recv_radius) {
+ if (sm)
+ sm->eap_if->aaaEapNoReq = true;
+ return;
+ }
+
+ msg = sm->last_recv_radius;
+
+ eap = radius_msg_get_eap(msg);
+ if (!eap) {
+ /* RFC 3579, Chap. 2.6.3:
+ * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+ * attribute */
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING,
+ "could not extract EAP-Message from RADIUS message");
+ sm->eap_if->aaaEapNoReq = true;
+ return;
+ }
+
+ if (wpabuf_len(eap) < sizeof(*hdr)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING,
+ "too short EAP packet received from authentication server");
+ wpabuf_free(eap);
+ sm->eap_if->aaaEapNoReq = true;
+ return;
+ }
+
+ if (wpabuf_len(eap) > sizeof(*hdr))
+ eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
+
+ hdr = wpabuf_head(eap);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_type >= 0)
+ sm->eap_type_authsrv = eap_type;
+ os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+ eap_server_get_name(0, eap_type), eap_type);
+ break;
+ case EAP_CODE_RESPONSE:
+ os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+ eap_server_get_name(0, eap_type), eap_type);
+ break;
+ case EAP_CODE_SUCCESS:
+ os_strlcpy(buf, "EAP Success", sizeof(buf));
+ break;
+ case EAP_CODE_FAILURE:
+ os_strlcpy(buf, "EAP Failure", sizeof(buf));
+ break;
+ default:
+ os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+ break;
+ }
+ buf[sizeof(buf) - 1] = '\0';
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "decapsulated EAP packet (code=%d id=%d len=%d) from RADIUS server: %s",
+ hdr->code, hdr->identifier, be_to_host16(hdr->length),
+ buf);
+ sm->eap_if->aaaEapReq = true;
+
+ wpabuf_free(sm->eap_if->aaaEapReqData);
+ sm->eap_if->aaaEapReqData = eap;
+}
+
+
+static void ieee802_1x_get_keys(struct hostapd_data *hapd,
+ struct sta_info *sta, struct radius_msg *msg,
+ struct radius_msg *req,
+ const u8 *shared_secret,
+ size_t shared_secret_len)
+{
+ struct radius_ms_mppe_keys *keys;
+ u8 *buf;
+ size_t len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+ shared_secret_len);
+
+ if (keys && keys->send && keys->recv) {
+ len = keys->send_len + keys->recv_len;
+ wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+ keys->send, keys->send_len);
+ wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+ keys->recv, keys->recv_len);
+
+ os_free(sm->eap_if->aaaEapKeyData);
+ sm->eap_if->aaaEapKeyData = os_malloc(len);
+ if (sm->eap_if->aaaEapKeyData) {
+ os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
+ keys->recv_len);
+ os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
+ keys->send, keys->send_len);
+ sm->eap_if->aaaEapKeyDataLen = len;
+ sm->eap_if->aaaEapKeyAvailable = true;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MS-MPPE: 1x_get_keys, could not get keys: %p send: %p recv: %p",
+ keys, keys ? keys->send : NULL,
+ keys ? keys->recv : NULL);
+ }
+
+ if (keys) {
+ os_free(keys->send);
+ os_free(keys->recv);
+ os_free(keys);
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+ NULL) == 0) {
+ os_free(sm->eap_if->eapSessionId);
+ sm->eap_if->eapSessionId = os_memdup(buf, len);
+ if (sm->eap_if->eapSessionId) {
+ sm->eap_if->eapSessionIdLen = len;
+ wpa_hexdump(MSG_DEBUG, "EAP-Key Name",
+ sm->eap_if->eapSessionId,
+ sm->eap_if->eapSessionIdLen);
+ }
+ } else {
+ sm->eap_if->eapSessionIdLen = 0;
+ }
+}
+
+
+static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u8 *attr_class;
+ size_t class_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ int count, i;
+ struct radius_attr_data *nclass;
+ size_t nclass_count;
+
+ if (!hapd->conf->radius->acct_server || !hapd->radius || !sm)
+ return;
+
+ radius_free_class(&sm->radius_class);
+ count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
+ if (count <= 0)
+ return;
+
+ nclass = os_calloc(count, sizeof(struct radius_attr_data));
+ if (!nclass)
+ return;
+
+ nclass_count = 0;
+
+ attr_class = NULL;
+ for (i = 0; i < count; i++) {
+ do {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
+ &attr_class, &class_len,
+ attr_class) < 0) {
+ i = count;
+ break;
+ }
+ } while (class_len < 1);
+
+ nclass[nclass_count].data = os_memdup(attr_class, class_len);
+ if (!nclass[nclass_count].data)
+ break;
+
+ nclass[nclass_count].len = class_len;
+ nclass_count++;
+ }
+
+ sm->radius_class.attr = nclass;
+ sm->radius_class.count = nclass_count;
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Stored %lu RADIUS Class attributes for "
+ MACSTR,
+ (unsigned long) sm->radius_class.count,
+ MAC2STR(sta->addr));
+}
+
+
+/* Update sta->identity based on User-Name attribute in Access-Accept */
+static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u8 *buf, *identity;
+ size_t len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
+ NULL) < 0)
+ return;
+
+ identity = (u8 *) dup_binstr(buf, len);
+ if (!identity)
+ return;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "old identity '%s' updated with User-Name from Access-Accept '%s'",
+ sm->identity ? (char *) sm->identity : "N/A",
+ (char *) identity);
+
+ os_free(sm->identity);
+ sm->identity = identity;
+ sm->identity_len = len;
+}
+
+
+/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
+static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ struct wpabuf *cui;
+ u8 *buf;
+ size_t len;
+
+ if (!sm)
+ return;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) < 0)
+ return;
+
+ cui = wpabuf_alloc_copy(buf, len);
+ if (!cui)
+ return;
+
+ wpabuf_free(sm->radius_cui);
+ sm->radius_cui = cui;
+}
+
+
+#ifdef CONFIG_HS20
+
+static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
+{
+ sta->remediation = 1;
+ os_free(sta->remediation_url);
+ if (len > 2) {
+ sta->remediation_url = os_malloc(len);
+ if (!sta->remediation_url)
+ return;
+ sta->remediation_method = pos[0];
+ os_memcpy(sta->remediation_url, pos + 1, len - 1);
+ sta->remediation_url[len - 1] = '\0';
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Subscription remediation needed for "
+ MACSTR " - server method %u URL %s",
+ MAC2STR(sta->addr), sta->remediation_method,
+ sta->remediation_url);
+ } else {
+ sta->remediation_url = NULL;
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Subscription remediation needed for "
+ MACSTR, MAC2STR(sta->addr));
+ }
+ /* TODO: assign the STA into remediation VLAN or add filtering */
+}
+
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *pos,
+ size_t len)
+{
+ size_t url_len;
+ unsigned int timeout;
+
+ if (len < 3)
+ return; /* Malformed information */
+ url_len = len - 3;
+ sta->hs20_deauth_requested = 1;
+ sta->hs20_deauth_on_ack = url_len == 0;
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Deauthentication request - Code %u Re-auth Delay %u URL length %zu",
+ *pos, WPA_GET_LE16(pos + 1), url_len);
+ wpabuf_free(sta->hs20_deauth_req);
+ sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+ if (sta->hs20_deauth_req) {
+ wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+ wpabuf_put_u8(sta->hs20_deauth_req, url_len);
+ wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
+ }
+ timeout = hapd->conf->hs20_deauth_req_timeout;
+ /* If there is no URL, no need to provide time to fetch it. Use a short
+ * timeout here to allow maximum time for completing 4-way handshake and
+ * WNM-Notification delivery. Acknowledgement of the frame will result
+ * in cutting this wait further. */
+ if (!url_len && timeout > 2)
+ timeout = 2;
+ ap_sta_session_timeout(hapd, sta, timeout);
+}
+
+
+static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len, int session_timeout)
+{
+ unsigned int swt;
+ int warning_time, beacon_int;
+
+ if (len < 1)
+ return; /* Malformed information */
+ os_free(sta->hs20_session_info_url);
+ sta->hs20_session_info_url = os_malloc(len);
+ if (!sta->hs20_session_info_url)
+ return;
+ swt = pos[0];
+ os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
+ sta->hs20_session_info_url[len - 1] = '\0';
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Session Information URL='%s' SWT=%u (session_timeout=%d)",
+ sta->hs20_session_info_url, swt, session_timeout);
+ if (session_timeout < 0) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: No Session-Timeout set - ignore session info URL");
+ return;
+ }
+ if (swt == 255)
+ swt = 1; /* Use one minute as the AP selected value */
+
+ if ((unsigned int) session_timeout < swt * 60)
+ warning_time = 0;
+ else
+ warning_time = session_timeout - swt * 60;
+
+ beacon_int = hapd->iconf->beacon_int;
+ if (beacon_int < 1)
+ beacon_int = 100; /* best guess */
+ sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
+ if (sta->hs20_disassoc_timer > 65535)
+ sta->hs20_disassoc_timer = 65535;
+
+ ap_sta_session_warning_timeout(hapd, sta, warning_time);
+}
+
+
+static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len)
+{
+ if (len < 4)
+ return; /* Malformed information */
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
+ pos[0], pos[1], pos[2], pos[3]);
+ hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
+}
+
+
+static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos, size_t len)
+{
+ os_free(sta->t_c_url);
+ sta->t_c_url = os_malloc(len + 1);
+ if (!sta->t_c_url)
+ return;
+ os_memcpy(sta->t_c_url, pos, len);
+ sta->t_c_url[len] = '\0';
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg,
+ int session_timeout)
+{
+#ifdef CONFIG_HS20
+ u8 *buf, *pos, *end, type, sublen;
+ size_t len;
+
+ buf = NULL;
+ sta->remediation = 0;
+ sta->hs20_deauth_requested = 0;
+ sta->hs20_deauth_on_ack = 0;
+
+ for (;;) {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, buf) < 0)
+ break;
+ if (len < 6)
+ continue;
+ pos = buf;
+ end = buf + len;
+ if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+ continue;
+ pos += 4;
+
+ type = *pos++;
+ sublen = *pos++;
+ if (sublen < 2)
+ continue; /* invalid length */
+ sublen -= 2; /* skip header */
+ if (pos + sublen > end)
+ continue; /* invalid WFA VSA */
+
+ switch (type) {
+ case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
+ ieee802_1x_hs20_sub_rem(sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+ ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
+ ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+ session_timeout);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
+ ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
+ ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
+ break;
+ }
+ }
+#endif /* CONFIG_HS20 */
+}
+
+
+struct sta_id_search {
+ u8 identifier;
+ struct eapol_state_machine *sm;
+};
+
+
+static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ void *ctx)
+{
+ struct sta_id_search *id_search = ctx;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm && sm->radius_identifier >= 0 &&
+ sm->radius_identifier == id_search->identifier) {
+ id_search->sm = sm;
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
+{
+ struct sta_id_search id_search;
+
+ id_search.identifier = identifier;
+ id_search.sm = NULL;
+ ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
+ return id_search.sm;
+}
+
+
+#ifndef CONFIG_NO_VLAN
+static int ieee802_1x_update_vlan(struct radius_msg *msg,
+ struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct vlan_description vlan_desc;
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged,
+ MAX_NUM_TAGGED_VLAN,
+ vlan_desc.tagged);
+
+ if (vlan_desc.notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ sta->eapol_sm->authFail = true;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ vlan_desc.untagged,
+ vlan_desc.tagged[0] ? "+" : "");
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ ap_sta_set_vlan(hapd, sta, &vlan_desc);
+ return -1;
+ }
+
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+ !vlan_desc.notempty) {
+ sta->eapol_sm->authFail = true;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "authentication server did not include required VLAN ID in Access-Accept");
+ return -1;
+ }
+
+ return ap_sta_set_vlan(hapd, sta, &vlan_desc);
+}
+#endif /* CONFIG_NO_VLAN */
+
+
+/**
+ * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ struct hostapd_data *hapd = data;
+ struct sta_info *sta;
+ u32 session_timeout = 0, termination_action, acct_interim_interval;
+ int session_timeout_set;
+ u32 reason_code;
+ struct eapol_state_machine *sm;
+ int override_eapReq = 0;
+ struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+ sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
+ if (!sm) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: Could not find matching station for this RADIUS message");
+ return RADIUS_RX_UNKNOWN;
+ }
+ sta = sm->sta;
+
+ /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+ * present when packet contains an EAP-Message attribute */
+ if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+ radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+ 0) < 0 &&
+ radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "Allowing RADIUS Access-Reject without Message-Authenticator since it does not include EAP-Message");
+ } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+ req, 1)) {
+ wpa_printf(MSG_INFO,
+ "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+ hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+ hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+ wpa_printf(MSG_INFO, "Unknown RADIUS message code");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ sm->radius_identifier = -1;
+ wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
+ MAC2STR(sta->addr));
+
+ radius_msg_free(sm->last_recv_radius);
+ sm->last_recv_radius = msg;
+
+ session_timeout_set =
+ !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+ &session_timeout);
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
+ &termination_action))
+ termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
+
+ if (hapd->conf->acct_interim_interval == 0 &&
+ hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+ &acct_interim_interval) == 0) {
+ if (acct_interim_interval < 60) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "ignored too small Acct-Interim-Interval %d",
+ acct_interim_interval);
+ } else
+ sta->acct_interim_interval = acct_interim_interval;
+ }
+
+
+ switch (hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+#ifndef CONFIG_NO_VLAN
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+ ieee802_1x_update_vlan(msg, hapd, sta) < 0)
+ break;
+
+ if (sta->vlan_id > 0) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "VLAN ID %d", sta->vlan_id);
+ }
+
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ ap_sta_bind_vlan(hapd, sta) < 0)
+ break;
+#endif /* CONFIG_NO_VLAN */
+
+ sta->session_timeout_set = !!session_timeout_set;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+
+ /* RFC 3580, Ch. 3.17 */
+ if (session_timeout_set && termination_action ==
+ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
+ sm->reAuthPeriod = session_timeout;
+ else if (session_timeout_set)
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ else
+ ap_sta_no_session_timeout(hapd, sta);
+
+ sm->eap_if->aaaSuccess = true;
+ override_eapReq = 1;
+ ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
+ shared_secret_len);
+ ieee802_1x_store_radius_class(hapd, sta, msg);
+ ieee802_1x_update_sta_identity(hapd, sta, msg);
+ ieee802_1x_update_sta_cui(hapd, sta, msg);
+ ieee802_1x_check_hs20(hapd, sta, msg,
+ session_timeout_set ?
+ (int) session_timeout : -1);
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ sm->eap_if->aaaFail = true;
+ override_eapReq = 1;
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+ &reason_code) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
+ MACSTR, reason_code, MAC2STR(sta->addr));
+ sta->disconnect_reason_code = reason_code;
+ }
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ sm->eap_if->aaaEapReq = true;
+ if (session_timeout_set) {
+ /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
+ sm->eap_if->aaaMethodTimeout = session_timeout;
+ hostapd_logger(hapd, sm->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "using EAP timeout of %d seconds (from RADIUS)",
+ sm->eap_if->aaaMethodTimeout);
+ } else {
+ /*
+ * Use dynamic retransmission behavior per EAP
+ * specification.
+ */
+ sm->eap_if->aaaMethodTimeout = 0;
+ }
+ break;
+ }
+
+ ieee802_1x_decapsulate_radius(hapd, sta);
+ if (override_eapReq)
+ sm->eap_if->aaaEapReq = false;
+
+#ifdef CONFIG_FILS
+#ifdef NEED_AP_MLME
+ if (sta->flags &
+ (WLAN_STA_PENDING_FILS_ERP | WLAN_STA_PENDING_PASN_FILS_ERP)) {
+ /* TODO: Add a PMKSA entry on success? */
+ ieee802_11_finish_fils_auth(
+ hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
+ sm->eap_if->aaaEapReqData,
+ sm->eap_if->aaaEapKeyData,
+ sm->eap_if->aaaEapKeyDataLen);
+ }
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_FILS */
+
+ eapol_auth_step(sm);
+
+ return RADIUS_RX_QUEUED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!sm)
+ return;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "aborting authentication");
+
+#ifndef CONFIG_NO_RADIUS
+ radius_msg_free(sm->last_recv_radius);
+ sm->last_recv_radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+ if (sm->eap_if->eapTimeout) {
+ /*
+ * Disconnect the STA since it did not reply to the last EAP
+ * request and we cannot continue EAP processing (EAP-Failure
+ * could only be sent if the EAP peer actually replied).
+ */
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
+ MAC2STR(sta->addr));
+
+ sm->eap_if->portEnabled = false;
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+}
+
+
+#ifdef CONFIG_WEP
+
+static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
+{
+ struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+ if (hapd->conf->default_wep_key_len < 1)
+ return 0;
+
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
+ if (!eapol->default_wep_key ||
+ random_get_bytes(eapol->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
+ wpa_printf(MSG_INFO, "Could not generate random WEP key");
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = NULL;
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len);
+
+ return 0;
+}
+
+
+static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (sta->eapol_sm) {
+ sta->eapol_sm->eap_if->eapKeyAvailable = true;
+ eapol_auth_step(sta->eapol_sm);
+ }
+ return 0;
+}
+
+
+static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+ if (eapol->default_wep_key_idx >= 3)
+ eapol->default_wep_key_idx =
+ hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+ else
+ eapol->default_wep_key_idx++;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+ eapol->default_wep_key_idx);
+
+ if (ieee802_1x_rekey_broadcast(hapd)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING,
+ "failed to generate a new broadcast key");
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = NULL;
+ return;
+ }
+
+ /* TODO: Could setup key for RX here, but change default TX keyid only
+ * after new broadcast key has been sent to all stations. */
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr,
+ eapol->default_wep_key_idx, 0, 1, NULL, 0,
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len,
+ KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING,
+ "failed to configure a new broadcast key");
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = NULL;
+ return;
+ }
+
+ ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
+
+ if (hapd->conf->wep_rekeying_period > 0) {
+ eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
+ ieee802_1x_rekey, hapd, NULL);
+ }
+}
+
+#endif /* CONFIG_WEP */
+
+
+static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
+ const u8 *data, size_t datalen)
+{
+#ifdef CONFIG_WPS
+ struct sta_info *sta = sta_ctx;
+
+ if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+ WLAN_STA_MAYBE_WPS) {
+ const u8 *identity;
+ size_t identity_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ identity = eap_get_identity(sm->eap, &identity_len);
+ if (identity &&
+ ((identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE,
+ WSC_ID_ENROLLEE_LEN) == 0) ||
+ (identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR,
+ WSC_ID_REGISTRAR_LEN) == 0))) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: WLAN_STA_MAYBE_WPS -> WLAN_STA_WPS");
+ sta->flags |= WLAN_STA_WPS;
+ }
+ }
+#endif /* CONFIG_WPS */
+
+ ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
+}
+
+
+static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
+ const u8 *data, size_t datalen)
+{
+#ifndef CONFIG_NO_RADIUS
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+
+ ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
+ int preauth, int remediation, bool logoff)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+
+ if (preauth) {
+ rsn_preauth_finished(hapd, sta, success);
+ return false;
+ }
+
+ return ieee802_1x_finished(hapd, sta, success, remediation, logoff);
+}
+
+
+static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ struct hostapd_data *hapd = ctx;
+ const struct hostapd_eap_user *eap_user;
+ int i;
+ int rv = -1;
+
+ eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
+ if (!eap_user)
+ goto out;
+
+ os_memset(user, 0, sizeof(*user));
+ user->phase2 = phase2;
+ for (i = 0; i < EAP_MAX_METHODS; i++) {
+ user->methods[i].vendor = eap_user->methods[i].vendor;
+ user->methods[i].method = eap_user->methods[i].method;
+ }
+
+ if (eap_user->password) {
+ user->password = os_memdup(eap_user->password,
+ eap_user->password_len);
+ if (!user->password)
+ goto out;
+ user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
+ }
+ user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
+ user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
+ rv = 0;
+
+out:
+ if (rv)
+ wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+ return rv;
+}
+
+
+static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->eapol_sm)
+ return 0;
+ return 1;
+}
+
+
+static void ieee802_1x_logger(void *ctx, const u8 *addr,
+ eapol_logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+ struct hostapd_data *hapd = ctx;
+ int hlevel;
+
+ switch (level) {
+ case EAPOL_LOGGER_WARNING:
+ hlevel = HOSTAPD_LEVEL_WARNING;
+ break;
+ case EAPOL_LOGGER_INFO:
+ hlevel = HOSTAPD_LEVEL_INFO;
+ break;
+ case EAPOL_LOGGER_DEBUG:
+ default:
+ hlevel = HOSTAPD_LEVEL_DEBUG;
+ break;
+ }
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
+ txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
+ int authorized)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+
+ ieee802_1x_set_sta_authorized(hapd, sta, authorized);
+}
+
+
+static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+
+ ieee802_1x_abort_auth(hapd, sta);
+}
+
+
+#ifdef CONFIG_WEP
+static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
+{
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+
+ ieee802_1x_tx_key(hapd, sta);
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+}
+#endif /* CONFIG_WEP */
+
+
+static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
+ enum eapol_event type)
+{
+ /* struct hostapd_data *hapd = ctx; */
+ struct sta_info *sta = sta_ctx;
+
+ switch (type) {
+ case EAPOL_AUTH_SM_CHANGE:
+ wpa_auth_sm_notify(sta->wpa_sm);
+ break;
+ case EAPOL_AUTH_REAUTHENTICATE:
+ wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+ break;
+ }
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key *
+ieee802_1x_erp_get_key(void *ctx, const char *keyname)
+{
+ struct hostapd_data *hapd = ctx;
+ struct eap_server_erp_key *erp;
+
+ dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
+ list) {
+ if (os_strcmp(erp->keyname_nai, keyname) == 0)
+ return erp;
+ }
+
+ return NULL;
+}
+
+
+static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+ struct hostapd_data *hapd = ctx;
+
+ dl_list_add(&hapd->erp_keys, &erp->list);
+ return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
+int ieee802_1x_init(struct hostapd_data *hapd)
+{
+ struct eapol_auth_config conf;
+ struct eapol_auth_cb cb;
+
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using IEEE 802.1X state machine of the first BSS");
+
+ hapd->eapol_auth = hapd->mld_first_bss->eapol_auth;
+ return 0;
+ }
+
+ dl_list_init(&hapd->erp_keys);
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.eap_cfg = hapd->eap_cfg;
+ conf.ctx = hapd;
+ conf.eap_reauth_period = hapd->conf->eap_reauth_period;
+ conf.wpa = hapd->conf->wpa;
+#ifdef CONFIG_WEP
+ conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
+#endif /* CONFIG_WEP */
+ conf.eap_req_id_text = hapd->conf->eap_req_id_text;
+ conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+ conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
+ conf.erp_domain = hapd->conf->erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+ conf.eap_skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memset(&cb, 0, sizeof(cb));
+ cb.eapol_send = ieee802_1x_eapol_send;
+ cb.aaa_send = ieee802_1x_aaa_send;
+ cb.finished = _ieee802_1x_finished;
+ cb.get_eap_user = ieee802_1x_get_eap_user;
+ cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
+ cb.logger = ieee802_1x_logger;
+ cb.set_port_authorized = ieee802_1x_set_port_authorized;
+ cb.abort_auth = _ieee802_1x_abort_auth;
+#ifdef CONFIG_WEP
+ cb.tx_key = _ieee802_1x_tx_key;
+#endif /* CONFIG_WEP */
+ cb.eapol_event = ieee802_1x_eapol_event;
+#ifdef CONFIG_ERP
+ cb.erp_get_key = ieee802_1x_erp_get_key;
+ cb.erp_add_key = ieee802_1x_erp_add_key;
+#endif /* CONFIG_ERP */
+
+ hapd->eapol_auth = eapol_auth_init(&conf, &cb);
+ if (!hapd->eapol_auth)
+ return -1;
+
+ if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+ return -1;
+
+#ifndef CONFIG_NO_RADIUS
+ if (radius_client_register(hapd->radius, RADIUS_AUTH,
+ ieee802_1x_receive_auth, hapd))
+ return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+#ifdef CONFIG_WEP
+ if (hapd->conf->default_wep_key_len) {
+ int i;
+
+ for (i = 0; i < 4; i++)
+ hostapd_drv_set_key(hapd->conf->iface, hapd,
+ WPA_ALG_NONE, NULL, i, 0, 0, NULL,
+ 0, NULL, 0, KEY_FLAG_GROUP);
+
+ ieee802_1x_rekey(hapd, NULL);
+
+ if (!hapd->eapol_auth->default_wep_key)
+ return -1;
+ }
+#endif /* CONFIG_WEP */
+
+ return 0;
+}
+
+
+void ieee802_1x_erp_flush(struct hostapd_data *hapd)
+{
+ struct eap_server_erp_key *erp;
+
+ while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
+ list)) != NULL) {
+ dl_list_del(&erp->list);
+ bin_clear_free(erp, sizeof(*erp));
+ }
+}
+
+
+void ieee802_1x_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->mld_first_bss) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Deinit IEEE 802.1X state machine of a non-first BSS");
+
+ hapd->eapol_auth = NULL;
+ return;
+ }
+
+#ifdef CONFIG_WEP
+ eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
+#endif /* CONFIG_WEP */
+
+ if (hapd->driver && hapd->drv_priv &&
+ (hapd->conf->ieee802_1x || hapd->conf->wpa))
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+ eapol_auth_deinit(hapd->eapol_auth);
+ hapd->eapol_auth = NULL;
+
+ ieee802_1x_erp_flush(hapd);
+}
+
+
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *buf, size_t len, int ack)
+{
+ struct ieee80211_hdr *hdr;
+ u8 *pos;
+ const unsigned char rfc1042_hdr[ETH_ALEN] =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+ if (!sta)
+ return -1;
+ if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
+ return 0;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ pos = (u8 *) (hdr + 1);
+ if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
+ return 0;
+ pos += sizeof(rfc1042_hdr);
+ if (WPA_GET_BE16(pos) != ETH_P_PAE)
+ return 0;
+ pos += 2;
+
+ return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos,
+ ack);
+}
+
+
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *buf, int len, int ack)
+{
+ const struct ieee802_1x_hdr *xhdr =
+ (const struct ieee802_1x_hdr *) buf;
+ const u8 *pos = buf + sizeof(*xhdr);
+ struct ieee802_1x_eapol_key *key;
+
+ if (len < (int) sizeof(*xhdr))
+ return 0;
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+ " TX status - version=%d type=%d length=%d - ack=%d",
+ MAC2STR(sta->addr), xhdr->version, xhdr->type,
+ be_to_host16(xhdr->length), ack);
+
+#ifdef CONFIG_WPS
+ if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
+ (sta->flags & WLAN_STA_WPS) &&
+ ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Indicate EAP completion on ACK for EAP-Failure");
+ hostapd_wps_eap_completed(hapd);
+ }
+#endif /* CONFIG_WPS */
+
+ if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
+ return 0;
+
+ if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+ const struct wpa_eapol_key *wpa;
+
+ wpa = (const struct wpa_eapol_key *) pos;
+ if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+ wpa->type == EAPOL_KEY_TYPE_WPA)
+ wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+ sta->wpa_sm, ack);
+ }
+
+ /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
+ * or Authenticator state machines, but EAPOL-Key packets are not
+ * retransmitted in case of failure. Try to re-send failed EAPOL-Key
+ * packets couple of times because otherwise STA keys become
+ * unsynchronized with AP. */
+ if (!ack && pos + sizeof(*key) <= buf + len) {
+ key = (struct ieee802_1x_eapol_key *) pos;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not Ack EAPOL-Key frame (%scast index=%d)",
+ key->key_index & BIT(7) ? "uni" : "broad",
+ key->key_index & ~BIT(7));
+ /* TODO: re-send EAPOL-Key couple of times (with short delay
+ * between them?). If all attempt fail, report error and
+ * deauthenticate STA so that it will get new keys when
+ * authenticating again (e.g., after returning in range).
+ * Separate limit/transmit state needed both for unicast and
+ * broadcast keys(?) */
+ }
+ /* TODO: could move unicast key configuration from ieee802_1x_tx_key()
+ * to here and change the key only if the EAPOL-Key packet was Acked.
+ */
+
+ return 1;
+}
+
+
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
+{
+ if (!sm || !sm->identity)
+ return NULL;
+
+ *len = sm->identity_len;
+ return sm->identity;
+}
+
+
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+ int idx)
+{
+ if (!sm || !sm->radius_class.attr ||
+ idx >= (int) sm->radius_class.count)
+ return NULL;
+
+ *len = sm->radius_class.attr[idx].len;
+ return sm->radius_class.attr[idx].data;
+}
+
+
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
+{
+ if (!sm)
+ return NULL;
+ return sm->radius_cui;
+}
+
+
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
+{
+ *len = 0;
+ if (!sm)
+ return NULL;
+
+ *len = sm->eap_if->eapKeyDataLen;
+ return sm->eap_if->eapKeyData;
+}
+
+
+#ifdef CONFIG_MACSEC
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+ size_t *len)
+{
+ *len = 0;
+ if (!sm || !sm->eap_if)
+ return NULL;
+
+ *len = sm->eap_if->eapSessionIdLen;
+ return sm->eap_if->eapSessionId;
+}
+#endif /* CONFIG_MACSEC */
+
+
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+ bool enabled)
+{
+ if (!sm)
+ return;
+ sm->eap_if->portEnabled = enabled;
+ eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, bool valid)
+{
+ if (!sm)
+ return;
+ sm->portValid = valid;
+ eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, bool pre_auth)
+{
+ if (!sm)
+ return;
+ if (pre_auth)
+ sm->flags |= EAPOL_SM_PREAUTH;
+ else
+ sm->flags &= ~EAPOL_SM_PREAUTH;
+}
+
+
+static const char * bool_txt(bool val)
+{
+ return val ? "TRUE" : "FALSE";
+}
+
+#ifdef CONFIG_CTRL_IFACE_MIB
+
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len = 0, ret;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ struct os_reltime diff;
+ const char *name1;
+ const char *name2;
+ char *identity_buf = NULL;
+
+ if (!sm)
+ return 0;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xPaePortNumber=%d\n"
+ "dot1xPaePortProtocolVersion=%d\n"
+ "dot1xPaePortCapabilities=1\n"
+ "dot1xPaePortInitialize=%d\n"
+ "dot1xPaePortReauthenticate=FALSE\n",
+ sta->aid,
+ EAPOL_VERSION,
+ sm->initialize);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* dot1xAuthConfigTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xAuthPaeState=%d\n"
+ "dot1xAuthBackendAuthState=%d\n"
+ "dot1xAuthAdminControlledDirections=%d\n"
+ "dot1xAuthOperControlledDirections=%d\n"
+ "dot1xAuthAuthControlledPortStatus=%d\n"
+ "dot1xAuthAuthControlledPortControl=%d\n"
+ "dot1xAuthQuietPeriod=%u\n"
+ "dot1xAuthServerTimeout=%u\n"
+ "dot1xAuthReAuthPeriod=%u\n"
+ "dot1xAuthReAuthEnabled=%s\n"
+ "dot1xAuthKeyTxEnabled=%s\n",
+ sm->auth_pae_state + 1,
+ sm->be_auth_state + 1,
+ sm->adminControlledDirections,
+ sm->operControlledDirections,
+ sm->authPortStatus,
+ sm->portControl,
+ sm->quietPeriod,
+ sm->serverTimeout,
+ sm->reAuthPeriod,
+ bool_txt(sm->reAuthEnabled),
+ bool_txt(sm->keyTxEnabled));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* dot1xAuthStatsTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xAuthEapolFramesRx=%u\n"
+ "dot1xAuthEapolFramesTx=%u\n"
+ "dot1xAuthEapolStartFramesRx=%u\n"
+ "dot1xAuthEapolLogoffFramesRx=%u\n"
+ "dot1xAuthEapolRespIdFramesRx=%u\n"
+ "dot1xAuthEapolRespFramesRx=%u\n"
+ "dot1xAuthEapolReqIdFramesTx=%u\n"
+ "dot1xAuthEapolReqFramesTx=%u\n"
+ "dot1xAuthInvalidEapolFramesRx=%u\n"
+ "dot1xAuthEapLengthErrorFramesRx=%u\n"
+ "dot1xAuthLastEapolFrameVersion=%u\n"
+ "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
+ sm->dot1xAuthEapolFramesRx,
+ sm->dot1xAuthEapolFramesTx,
+ sm->dot1xAuthEapolStartFramesRx,
+ sm->dot1xAuthEapolLogoffFramesRx,
+ sm->dot1xAuthEapolRespIdFramesRx,
+ sm->dot1xAuthEapolRespFramesRx,
+ sm->dot1xAuthEapolReqIdFramesTx,
+ sm->dot1xAuthEapolReqFramesTx,
+ sm->dot1xAuthInvalidEapolFramesRx,
+ sm->dot1xAuthEapLengthErrorFramesRx,
+ sm->dot1xAuthLastEapolFrameVersion,
+ MAC2STR(sm->addr));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* dot1xAuthDiagTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xAuthEntersConnecting=%u\n"
+ "dot1xAuthEapLogoffsWhileConnecting=%u\n"
+ "dot1xAuthEntersAuthenticating=%u\n"
+ "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
+ "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
+ "dot1xAuthAuthFailWhileAuthenticating=%u\n"
+ "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
+ "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
+ "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
+ "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
+ "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
+ "dot1xAuthBackendResponses=%u\n"
+ "dot1xAuthBackendAccessChallenges=%u\n"
+ "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
+ "dot1xAuthBackendAuthSuccesses=%u\n"
+ "dot1xAuthBackendAuthFails=%u\n",
+ sm->authEntersConnecting,
+ sm->authEapLogoffsWhileConnecting,
+ sm->authEntersAuthenticating,
+ sm->authAuthSuccessesWhileAuthenticating,
+ sm->authAuthTimeoutsWhileAuthenticating,
+ sm->authAuthFailWhileAuthenticating,
+ sm->authAuthEapStartsWhileAuthenticating,
+ sm->authAuthEapLogoffWhileAuthenticating,
+ sm->authAuthReauthsWhileAuthenticated,
+ sm->authAuthEapStartsWhileAuthenticated,
+ sm->authAuthEapLogoffWhileAuthenticated,
+ sm->backendResponses,
+ sm->backendAccessChallenges,
+ sm->backendOtherRequestsToSupplicant,
+ sm->backendAuthSuccesses,
+ sm->backendAuthFails);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* dot1xAuthSessionStatsTable */
+ os_reltime_age(&sta->acct_session_start, &diff);
+ if (sm->eap && !sm->identity) {
+ const u8 *id;
+ size_t id_len;
+
+ id = eap_get_identity(sm->eap, &id_len);
+ if (id)
+ identity_buf = dup_binstr(id, id_len);
+ }
+ ret = os_snprintf(buf + len, buflen - len,
+ /* TODO: dot1xAuthSessionOctetsRx */
+ /* TODO: dot1xAuthSessionOctetsTx */
+ /* TODO: dot1xAuthSessionFramesRx */
+ /* TODO: dot1xAuthSessionFramesTx */
+ "dot1xAuthSessionId=%016llX\n"
+ "dot1xAuthSessionAuthenticMethod=%d\n"
+ "dot1xAuthSessionTime=%u\n"
+ "dot1xAuthSessionTerminateCause=999\n"
+ "dot1xAuthSessionUserName=%s\n",
+ (unsigned long long) sta->acct_session_id,
+ (wpa_key_mgmt_wpa_ieee8021x(
+ wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
+ 1 : 2,
+ (unsigned int) diff.sec,
+ sm->identity ? (char *) sm->identity :
+ (identity_buf ? identity_buf : "N/A"));
+ os_free(identity_buf);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ if (sm->acct_multi_session_id) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "authMultiSessionId=%016llX\n",
+ (unsigned long long)
+ sm->acct_multi_session_id);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ name1 = eap_server_get_name(0, sm->eap_type_authsrv);
+ name2 = eap_server_get_name(0, sm->eap_type_supp);
+ ret = os_snprintf(buf + len, buflen - len,
+ "last_eap_type_as=%d (%s)\n"
+ "last_eap_type_sta=%d (%s)\n",
+ sm->eap_type_authsrv, name1,
+ sm->eap_type_supp, name2);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+
+#endif
+
+#ifdef CONFIG_HS20
+static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (sta->remediation) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+ MACSTR " to indicate Subscription Remediation",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification(hapd, sta->addr,
+ sta->remediation_method,
+ sta->remediation_url);
+ os_free(sta->remediation_url);
+ sta->remediation_url = NULL;
+ }
+
+ if (sta->hs20_deauth_req) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+ MACSTR " to indicate imminent deauthentication",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
+ sta->hs20_deauth_req);
+ }
+
+ if (sta->hs20_t_c_filtering) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+ MACSTR " to indicate Terms and Conditions filtering",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
+ os_free(sta->t_c_url);
+ sta->t_c_url = NULL;
+ }
+}
+#endif /* CONFIG_HS20 */
+
+
+static bool ieee802_1x_finished(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ int remediation, bool logoff)
+{
+ const u8 *key;
+ size_t len;
+ /* TODO: get PMKLifetime from WPA parameters */
+ static const int dot11RSNAConfigPMKLifetime = 43200;
+ unsigned int session_timeout;
+ struct os_reltime now, remaining;
+
+#ifdef CONFIG_HS20
+ if (remediation && !sta->remediation) {
+ sta->remediation = 1;
+ os_free(sta->remediation_url);
+ sta->remediation_url =
+ os_strdup(hapd->conf->subscr_remediation_url);
+ sta->remediation_method = 1; /* SOAP-XML SPP */
+ }
+
+ if (success && (sta->remediation || sta->hs20_deauth_req ||
+ sta->hs20_t_c_filtering)) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
+ MACSTR " in 100 ms", MAC2STR(sta->addr));
+ eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
+ eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send,
+ hapd, sta);
+ }
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MACSEC
+ ieee802_1x_notify_create_actor_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &len);
+ if (sta->session_timeout_set) {
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+ session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
+ } else {
+ session_timeout = dot11RSNAConfigPMKLifetime;
+ }
+ if (success && key && len >= PMK_LEN && !sta->remediation &&
+ !sta->hs20_deauth_requested &&
+ wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
+ sta->eapol_sm) == 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "Added PMKSA cache entry (IEEE 802.1X)");
+ }
+
+ if (!success) {
+ /*
+ * Many devices require deauthentication after WPS provisioning
+ * and some may not be be able to do that themselves, so
+ * disconnect the client here. In addition, this may also
+ * benefit IEEE 802.1X/EAPOL authentication cases, too since
+ * the EAPOL PAE state machine would remain in HELD state for
+ * considerable amount of time and some EAP methods, like
+ * EAP-FAST with anonymous provisioning, may require another
+ * EAPOL authentication to be started to complete connection.
+ */
+ ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta,
+ logoff ? 0 : 10);
+ if (logoff && sta->wpa_sm)
+ return true;
+ }
+
+ return false;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.h
new file mode 100644
index 0000000..1469351
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.h
@@ -0,0 +1,69 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_H
+#define IEEE802_1X_H
+
+struct hostapd_data;
+struct sta_info;
+struct eapol_state_machine;
+struct hostapd_config;
+struct hostapd_bss_config;
+struct hostapd_radius_attr;
+struct radius_msg;
+
+
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+ size_t len, enum frame_encryption encrypted);
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
+int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_erp_flush(struct hostapd_data *hapd);
+void ieee802_1x_deinit(struct hostapd_data *hapd);
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *buf, size_t len, int ack);
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *data, int len, int ack);
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+ int idx);
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+ size_t *len);
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+ bool enabled);
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, bool valid);
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, bool pre_auth);
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+void hostapd_get_ntp_timestamp(u8 *buf);
+char *eap_type_text(u8 type);
+
+const char *radius_mode_txt(struct hostapd_data *hapd);
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg);
+int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
+ struct radius_msg *msg, int acct);
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len);
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* IEEE802_1X_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.c
new file mode 100644
index 0000000..43b0bf1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.c
@@ -0,0 +1,244 @@
+/*
+ * hostapd - MBO
+ * Copyright (c) 2016, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "mbo_ap.h"
+
+
+void mbo_ap_sta_free(struct sta_info *sta)
+{
+ struct mbo_non_pref_chan_info *info, *prev;
+
+ info = sta->non_pref_chan;
+ sta->non_pref_chan = NULL;
+ while (info) {
+ prev = info;
+ info = info->next;
+ os_free(prev);
+ }
+}
+
+
+static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
+ const u8 *buf, size_t len)
+{
+ struct mbo_non_pref_chan_info *info, *tmp;
+ char channels[200], *pos, *end;
+ size_t num_chan, i;
+ int ret;
+
+ if (len <= 3)
+ return; /* Not enough room for any channels */
+
+ num_chan = len - 3;
+ info = os_zalloc(sizeof(*info) + num_chan);
+ if (!info)
+ return;
+ info->op_class = buf[0];
+ info->pref = buf[len - 2];
+ info->reason_code = buf[len - 1];
+ info->num_channels = num_chan;
+ buf++;
+ os_memcpy(info->channels, buf, num_chan);
+ if (!sta->non_pref_chan) {
+ sta->non_pref_chan = info;
+ } else {
+ tmp = sta->non_pref_chan;
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = info;
+ }
+
+ pos = channels;
+ end = pos + sizeof(channels);
+ *pos = '\0';
+ for (i = 0; i < num_chan; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i == 0 ? "" : " ", buf[i]);
+ if (os_snprintf_error(end - pos, ret)) {
+ *pos = '\0';
+ break;
+ }
+ pos += ret;
+ }
+
+ wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
+ " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
+ MAC2STR(sta->addr), info->op_class, info->pref,
+ info->reason_code, channels);
+}
+
+
+void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ const u8 *pos, *attr, *end;
+ size_t len;
+
+ if (!hapd->conf->mbo_enabled || !elems->mbo)
+ return;
+
+ pos = elems->mbo + 4;
+ len = elems->mbo_len - 4;
+ wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
+
+ attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
+ if (attr && attr[1] >= 1)
+ sta->cell_capa = attr[2];
+
+ mbo_ap_sta_free(sta);
+ end = pos + len;
+ while (end - pos > 1) {
+ u8 ie_len = pos[1];
+
+ if (2 + ie_len > end - pos)
+ break;
+
+ if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
+ mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
+ pos += 2 + pos[1];
+ }
+}
+
+
+int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
+{
+ char *pos = buf, *end = buf + buflen;
+ int ret;
+ struct mbo_non_pref_chan_info *info;
+ u8 i;
+ unsigned int count = 0;
+
+ if (!sta->cell_capa)
+ return 0;
+
+ ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ for (info = sta->non_pref_chan; info; info = info->next) {
+ char *pos2 = pos;
+
+ ret = os_snprintf(pos2, end - pos2,
+ "non_pref_chan[%u]=%u:%u:%u:",
+ count, info->op_class, info->pref,
+ info->reason_code);
+ count++;
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+
+ for (i = 0; i < info->num_channels; i++) {
+ ret = os_snprintf(pos2, end - pos2, "%u%s",
+ info->channels[i],
+ i + 1 < info->num_channels ?
+ "," : "");
+ if (os_snprintf_error(end - pos2, ret)) {
+ pos2 = NULL;
+ break;
+ }
+ pos2 += ret;
+ }
+
+ if (!pos2)
+ break;
+ ret = os_snprintf(pos2, end - pos2, "\n");
+ if (os_snprintf_error(end - pos2, ret))
+ break;
+ pos2 += ret;
+ pos = pos2;
+ }
+
+ return pos - buf;
+}
+
+
+static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
+ const u8 *buf, size_t len)
+{
+ if (len < 1)
+ return;
+ wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
+ " updated cellular data capability: %u",
+ MAC2STR(sta->addr), buf[0]);
+ sta->cell_capa = buf[0];
+}
+
+
+static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
+ const u8 *buf, size_t len,
+ int *first_non_pref_chan)
+{
+ switch (type) {
+ case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
+ if (*first_non_pref_chan) {
+ /*
+ * Need to free the previously stored entries now to
+ * allow the update to replace all entries.
+ */
+ *first_non_pref_chan = 0;
+ mbo_ap_sta_free(sta);
+ }
+ mbo_ap_parse_non_pref_chan(sta, buf, len);
+ break;
+ case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
+ mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "MBO: Ignore unknown WNM Notification WFA subelement %u",
+ type);
+ break;
+ }
+}
+
+
+void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ u8 ie_len;
+ struct sta_info *sta;
+ int first_non_pref_chan = 1;
+
+ if (!hapd->conf->mbo_enabled)
+ return;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return;
+
+ pos = buf;
+ end = buf + len;
+
+ while (end - pos > 1) {
+ ie_len = pos[1];
+
+ if (2 + ie_len > end - pos)
+ break;
+
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
+ mbo_ap_wnm_notif_req_elem(sta, pos[5],
+ pos + 6, ie_len - 4,
+ &first_non_pref_chan);
+ else
+ wpa_printf(MSG_DEBUG,
+ "MBO: Ignore unknown WNM Notification element %u (len=%u)",
+ pos[0], pos[1]);
+
+ pos += 2 + pos[1];
+ }
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.h
new file mode 100644
index 0000000..9f37f28
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.h
@@ -0,0 +1,51 @@
+/*
+ * MBO related functions and structures
+ * Copyright (c) 2016, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MBO_AP_H
+#define MBO_AP_H
+
+struct hostapd_data;
+struct sta_info;
+struct ieee802_11_elems;
+
+#ifdef CONFIG_MBO
+
+void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
+ struct ieee802_11_elems *elems);
+int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
+void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len);
+void mbo_ap_sta_free(struct sta_info *sta);
+
+#else /* CONFIG_MBO */
+
+static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+}
+
+static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+
+static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const u8 *buf, size_t len)
+{
+}
+
+static inline void mbo_ap_sta_free(struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_MBO */
+
+#endif /* MBO_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.c
new file mode 100644
index 0000000..bc1eb62
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.c
@@ -0,0 +1,189 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "list.h"
+#include "x_snoop.h"
+#include "ndisc_snoop.h"
+
+struct ip6addr {
+ struct in6_addr addr;
+ struct dl_list list;
+};
+
+struct icmpv6_ndmsg {
+ struct ip6_hdr ipv6h;
+ struct icmp6_hdr icmp6h;
+ struct in6_addr target_addr;
+ u8 opt_type;
+ u8 len;
+ u8 opt_lladdr[0];
+} STRUCT_PACKED;
+
+#define ROUTER_ADVERTISEMENT 134
+#define NEIGHBOR_SOLICITATION 135
+#define NEIGHBOR_ADVERTISEMENT 136
+#define SOURCE_LL_ADDR 1
+
+static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
+{
+ struct ip6addr *ip6addr;
+
+ ip6addr = os_zalloc(sizeof(*ip6addr));
+ if (!ip6addr)
+ return -1;
+
+ os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
+
+ dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
+
+ return 0;
+}
+
+
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct ip6addr *ip6addr, *prev;
+
+ dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+ list) {
+ hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+ dl_list_del(&ip6addr->list);
+ os_free(ip6addr);
+ }
+}
+
+
+static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
+{
+ struct ip6addr *ip6addr;
+
+ dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
+ if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
+ ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
+ ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
+ ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ continue;
+ x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len);
+ }
+}
+
+
+static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct icmpv6_ndmsg *msg;
+ struct in6_addr saddr;
+ struct sta_info *sta;
+ int res;
+ char addrtxt[INET6_ADDRSTRLEN + 1];
+
+ if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+ return;
+ msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
+ switch (msg->icmp6h.icmp6_type) {
+ case NEIGHBOR_SOLICITATION:
+ if (len < ETH_HLEN + sizeof(*msg))
+ return;
+ if (msg->opt_type != SOURCE_LL_ADDR)
+ return;
+
+ /*
+ * IPv6 header may not be 32-bit aligned in the buffer, so use
+ * a local copy to avoid unaligned reads.
+ */
+ os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
+ if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
+ saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
+ if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
+ return;
+ sta = ap_get_sta(hapd, msg->opt_lladdr);
+ if (!sta)
+ return;
+
+ if (sta_has_ip6addr(sta, &saddr))
+ return;
+
+ if (inet_ntop(AF_INET6, &saddr, addrtxt,
+ sizeof(addrtxt)) == NULL)
+ addrtxt[0] = '\0';
+ wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
+ MACSTR, addrtxt, MAC2STR(sta->addr));
+ hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
+ res = hostapd_drv_br_add_ip_neigh(hapd, 6,
+ (u8 *) &saddr,
+ 128, sta->addr);
+ if (res) {
+ wpa_printf(MSG_ERROR,
+ "ndisc_snoop: Adding ip neigh failed: %d",
+ res);
+ return;
+ }
+
+ if (sta_ip6addr_add(sta, &saddr))
+ return;
+ }
+ break;
+#ifdef CONFIG_HS20
+ case ROUTER_ADVERTISEMENT:
+ if (hapd->conf->disable_dgaf)
+ ucast_to_stas(hapd, buf, len);
+ break;
+#endif /* CONFIG_HS20 */
+ case NEIGHBOR_ADVERTISEMENT:
+ if (hapd->conf->na_mcast_to_ucast)
+ ucast_to_stas(hapd, buf, len);
+ break;
+ default:
+ break;
+ }
+}
+
+
+int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+ hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
+ L2_PACKET_FILTER_NDISC);
+ if (hapd->sock_ndisc == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+ l2_packet_deinit(hapd->sock_ndisc);
+ hapd->sock_ndisc = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.h
new file mode 100644
index 0000000..3cc9a55
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.h
@@ -0,0 +1,36 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NDISC_SNOOP_H
+#define NDISC_SNOOP_H
+
+#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
+
+int ndisc_snoop_init(struct hostapd_data *hapd);
+void ndisc_snoop_deinit(struct hostapd_data *hapd);
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+static inline int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void sta_ip6addr_del(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+#endif /* NDISC_SNOOP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.c
new file mode 100644
index 0000000..5b276e8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.c
@@ -0,0 +1,327 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/crc32.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "neighbor_db.h"
+
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+ const struct wpa_ssid_value *ssid)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+ (!ssid ||
+ (ssid->ssid_len == nr->ssid.ssid_len &&
+ os_memcmp(ssid->ssid, nr->ssid.ssid,
+ ssid->ssid_len) == 0)))
+ return nr;
+ }
+ return NULL;
+}
+
+
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ struct hostapd_neighbor_entry *nr;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ int ret;
+ char nrie[2 * 255 + 1];
+ char lci[2 * 255 + 1];
+ char civic[2 * 255 + 1];
+ char ssid[SSID_MAX_LEN * 2 + 1];
+
+ ssid[0] = '\0';
+ wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
+ nr->ssid.ssid_len);
+
+ nrie[0] = '\0';
+ if (nr->nr)
+ wpa_snprintf_hex(nrie, sizeof(nrie),
+ wpabuf_head(nr->nr),
+ wpabuf_len(nr->nr));
+
+ lci[0] = '\0';
+ if (nr->lci)
+ wpa_snprintf_hex(lci, sizeof(lci),
+ wpabuf_head(nr->lci),
+ wpabuf_len(nr->lci));
+
+ civic[0] = '\0';
+ if (nr->civic)
+ wpa_snprintf_hex(civic, sizeof(civic),
+ wpabuf_head(nr->civic),
+ wpabuf_len(nr->civic));
+
+ ret = os_snprintf(pos, end - pos, MACSTR
+ " ssid=%s%s%s%s%s%s%s%s\n",
+ MAC2STR(nr->bssid), ssid,
+ nr->nr ? " nr=" : "", nrie,
+ nr->lci ? " lci=" : "", lci,
+ nr->civic ? " civic=" : "", civic,
+ nr->stationary ? " stat" : "");
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
+{
+ wpabuf_free(nr->nr);
+ nr->nr = NULL;
+ wpabuf_free(nr->lci);
+ nr->lci = NULL;
+ wpabuf_free(nr->civic);
+ nr->civic = NULL;
+ os_memset(nr->bssid, 0, sizeof(nr->bssid));
+ os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+ nr->stationary = 0;
+}
+
+
+static struct hostapd_neighbor_entry *
+hostapd_neighbor_add(struct hostapd_data *hapd)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
+ if (!nr)
+ return NULL;
+
+ dl_list_add(&hapd->nr_db, &nr->list);
+
+ return nr;
+}
+
+
+int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
+ const struct wpa_ssid_value *ssid,
+ const struct wpabuf *nr, const struct wpabuf *lci,
+ const struct wpabuf *civic, int stationary,
+ u8 bss_parameters)
+{
+ struct hostapd_neighbor_entry *entry;
+
+ entry = hostapd_neighbor_get(hapd, bssid, ssid);
+ if (!entry)
+ entry = hostapd_neighbor_add(hapd);
+ if (!entry)
+ return -1;
+
+ hostapd_neighbor_clear_entry(entry);
+
+ os_memcpy(entry->bssid, bssid, ETH_ALEN);
+ os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
+ entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
+
+ entry->nr = wpabuf_dup(nr);
+ if (!entry->nr)
+ goto fail;
+
+ if (lci && wpabuf_len(lci)) {
+ entry->lci = wpabuf_dup(lci);
+ if (!entry->lci || os_get_time(&entry->lci_date))
+ goto fail;
+ }
+
+ if (civic && wpabuf_len(civic)) {
+ entry->civic = wpabuf_dup(civic);
+ if (!entry->civic)
+ goto fail;
+ }
+
+ entry->stationary = stationary;
+ entry->bss_parameters = bss_parameters;
+
+ return 0;
+
+fail:
+ hostapd_neighbor_remove(hapd, bssid, ssid);
+ return -1;
+}
+
+
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+ const struct wpa_ssid_value *ssid)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ nr = hostapd_neighbor_get(hapd, bssid, ssid);
+ if (!nr)
+ return -1;
+
+ hostapd_neighbor_clear_entry(nr);
+ dl_list_del(&nr->list);
+ os_free(nr);
+
+ return 0;
+}
+
+
+void hostapd_free_neighbor_db(struct hostapd_data *hapd)
+{
+ struct hostapd_neighbor_entry *nr, *prev;
+
+ dl_list_for_each_safe(nr, prev, &hapd->nr_db,
+ struct hostapd_neighbor_entry, list) {
+ hostapd_neighbor_clear_entry(nr);
+ dl_list_del(&nr->list);
+ os_free(nr);
+ }
+}
+
+
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+ int ht, int vht, int he)
+{
+ enum oper_chan_width oper_chwidth;
+
+ oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+
+ if (!ht && !vht && !he)
+ return NR_CHAN_WIDTH_20;
+ if (!hapd->iconf->secondary_channel)
+ return NR_CHAN_WIDTH_20;
+ if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+ return NR_CHAN_WIDTH_40;
+ if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+ return NR_CHAN_WIDTH_80;
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
+ return NR_CHAN_WIDTH_160;
+ if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
+ return NR_CHAN_WIDTH_80P80;
+ return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+ u16 capab = hostapd_own_capab_info(hapd);
+ int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+ int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+ int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
+ bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
+ struct wpa_ssid_value ssid;
+ u8 channel, op_class;
+ u8 center_freq1_idx = 0, center_freq2_idx = 0;
+ enum nr_chan_width width;
+ u32 bssid_info;
+ struct wpabuf *nr;
+
+ if (!(hapd->conf->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+ return;
+
+ bssid_info = 3; /* AP is reachable */
+ bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+ bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+ if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+ bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+ bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+ if (hapd->conf->wmm_enabled) {
+ bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+ if (hapd->conf->wmm_uapsd &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+ bssid_info |= NEI_REP_BSSID_INFO_APSD;
+ }
+
+ if (ht) {
+ bssid_info |= NEI_REP_BSSID_INFO_HT |
+ NEI_REP_BSSID_INFO_DELAYED_BA;
+
+ /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+ if (vht)
+ bssid_info |= NEI_REP_BSSID_INFO_VHT;
+ }
+
+ if (he)
+ bssid_info |= NEI_REP_BSSID_INFO_HE;
+ if (eht)
+ bssid_info |= NEI_REP_BSSID_INFO_EHT;
+ /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return;
+ width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
+ if (vht) {
+ center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
+ hapd->iconf);
+ if (width == NR_CHAN_WIDTH_80P80)
+ center_freq2_idx =
+ hostapd_get_oper_centr_freq_seg1_idx(
+ hapd->iconf);
+ } else if (ht) {
+ ieee80211_freq_to_chan(hapd->iface->freq +
+ 10 * hapd->iconf->secondary_channel,
+ ¢er_freq1_idx);
+ }
+
+ ssid.ssid_len = hapd->conf->ssid.ssid_len;
+ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+ /*
+ * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+ * phy type + wide bandwidth channel subelement.
+ */
+ nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+ if (!nr)
+ return;
+
+ wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+ wpabuf_put_le32(nr, bssid_info);
+ wpabuf_put_u8(nr, op_class);
+ wpabuf_put_u8(nr, channel);
+ wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+ /*
+ * Wide Bandwidth Channel subelement may be needed to allow the
+ * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+ * Figure 9-301.
+ */
+ wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
+ wpabuf_put_u8(nr, 3);
+ wpabuf_put_u8(nr, width);
+ wpabuf_put_u8(nr, center_freq1_idx);
+ wpabuf_put_u8(nr, center_freq2_idx);
+
+ hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+ hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
+
+ wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.h
new file mode 100644
index 0000000..992671b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.h
@@ -0,0 +1,27 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NEIGHBOR_DB_H
+#define NEIGHBOR_DB_H
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+ const struct wpa_ssid_value *ssid);
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen);
+int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
+ const struct wpa_ssid_value *ssid,
+ const struct wpabuf *nr, const struct wpabuf *lci,
+ const struct wpabuf *civic, int stationary,
+ u8 bss_parameters);
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+ const struct wpa_ssid_value *ssid);
+void hostapd_free_neighbor_db(struct hostapd_data *hapd);
+
+#endif /* NEIGHBOR_DB_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..9be640c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.c
@@ -0,0 +1,113 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ if (sta->p2p_ie == NULL)
+ return 0;
+
+ return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+ "duration=%d", count, start, duration);
+
+ if (count == 0) {
+ hapd->noa_enabled = 0;
+ hapd->noa_start = 0;
+ hapd->noa_duration = 0;
+ }
+
+ if (count != 255) {
+ wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+ "NoA parameters");
+ return hostapd_driver_set_noa(hapd, count, start, duration);
+ }
+
+ hapd->noa_enabled = 1;
+ hapd->noa_start = start;
+ hapd->noa_duration = duration;
+
+ if (hapd->num_sta_no_p2p == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+ "periodic NoA parameters");
+ return hostapd_driver_set_noa(hapd, count, start, duration);
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+ "periodic NoA");
+
+ return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+ if (hapd->noa_enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+ hostapd_driver_set_noa(hapd, 0, 0, 0);
+ }
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+ if (hapd->noa_enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+ hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+ hapd->noa_duration);
+ }
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 bitmap;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ *eid++ = 4 + 3 + 1;
+ WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+ eid += 4;
+
+ *eid++ = P2P_ATTR_MANAGEABILITY;
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+ if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+ bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+ bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+ *eid++ = bitmap;
+
+ return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..0e3921c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.c
new file mode 100644
index 0000000..32d291d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.c
@@ -0,0 +1,736 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "pmksa_cache_auth.h"
+
+
+static const int pmksa_cache_max_entries = 1024;
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_pmksa_cache {
+#define PMKID_HASH_SIZE 128
+#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
+ struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
+ struct rsn_pmksa_cache_entry *pmksa;
+ int pmksa_count;
+
+ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
+ void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+ os_free(entry->vlan_desc);
+ os_free(entry->identity);
+ os_free(entry->dpp_pkhash);
+ wpabuf_free(entry->cui);
+#ifndef CONFIG_NO_RADIUS
+ radius_free_class(&entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+ bin_clear_free(entry, sizeof(*entry));
+}
+
+
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos, *prev;
+ unsigned int hash;
+
+ pmksa->pmksa_count--;
+ pmksa->free_cb(entry, pmksa->ctx);
+
+ /* unlink from hash list */
+ hash = PMKID_HASH(entry->pmkid);
+ pos = pmksa->pmkid[hash];
+ prev = NULL;
+ while (pos) {
+ if (pos == entry) {
+ if (prev != NULL)
+ prev->hnext = entry->hnext;
+ else
+ pmksa->pmkid[hash] = entry->hnext;
+ break;
+ }
+ prev = pos;
+ pos = pos->hnext;
+ }
+
+ /* unlink from entry list */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos == entry) {
+ if (prev != NULL)
+ prev->next = entry->next;
+ else
+ pmksa->pmksa = entry->next;
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+
+ _pmksa_cache_free_entry(entry);
+}
+
+
+/**
+ * pmksa_cache_auth_flush - Flush all PMKSA cache entries
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
+{
+ while (pmksa->pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
+ MACSTR, MAC2STR(pmksa->pmksa->spa));
+ pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+ }
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(pmksa->pmksa->spa));
+ pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+ }
+
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+ int sec;
+ struct os_reltime now;
+
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ if (pmksa->pmksa == NULL)
+ return;
+ os_get_reltime(&now);
+ sec = pmksa->pmksa->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+}
+
+
+static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
+{
+ struct vlan_description *vlan_desc;
+
+ if (eapol == NULL)
+ return;
+
+ if (eapol->identity) {
+ entry->identity = os_malloc(eapol->identity_len);
+ if (entry->identity) {
+ entry->identity_len = eapol->identity_len;
+ os_memcpy(entry->identity, eapol->identity,
+ eapol->identity_len);
+ }
+ }
+
+ if (eapol->radius_cui)
+ entry->cui = wpabuf_dup(eapol->radius_cui);
+
+#ifndef CONFIG_NO_RADIUS
+ radius_copy_class(&entry->radius_class, &eapol->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+ entry->eap_type_authsrv = eapol->eap_type_authsrv;
+
+ vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
+ if (vlan_desc && vlan_desc->notempty) {
+ entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+ if (entry->vlan_desc)
+ *entry->vlan_desc = *vlan_desc;
+ } else {
+ entry->vlan_desc = NULL;
+ }
+
+ entry->acct_multi_session_id = eapol->acct_multi_session_id;
+}
+
+
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+ struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
+{
+ if (entry == NULL || eapol == NULL)
+ return;
+
+ if (entry->identity) {
+ os_free(eapol->identity);
+ eapol->identity = os_malloc(entry->identity_len);
+ if (eapol->identity) {
+ eapol->identity_len = entry->identity_len;
+ os_memcpy(eapol->identity, entry->identity,
+ entry->identity_len);
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
+ eapol->identity, eapol->identity_len);
+ }
+
+ if (entry->cui) {
+ wpabuf_free(eapol->radius_cui);
+ eapol->radius_cui = wpabuf_dup(entry->cui);
+ }
+
+#ifndef CONFIG_NO_RADIUS
+ radius_free_class(&eapol->radius_class);
+ radius_copy_class(&eapol->radius_class, &entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+ if (eapol->radius_class.attr) {
+ wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
+ "PMKSA", (unsigned long) eapol->radius_class.count);
+ }
+
+ eapol->eap_type_authsrv = entry->eap_type_authsrv;
+#ifndef CONFIG_NO_VLAN
+ ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
+#endif /* CONFIG_NO_VLAN */
+
+ eapol->acct_multi_session_id = entry->acct_multi_session_id;
+}
+
+
+static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos, *prev;
+ int hash;
+
+ /* Add the new entry; order by expiration time */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = pmksa->pmksa;
+ pmksa->pmksa = entry;
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+
+ hash = PMKID_HASH(entry->pmkid);
+ entry->hnext = pmksa->pmkid[hash];
+ pmksa->pmkid[hash] = entry;
+
+ pmksa->pmksa_count++;
+ if (prev == NULL)
+ pmksa_cache_set_expiration(pmksa);
+ wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+ MAC2STR(entry->spa));
+ wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
+}
+
+
+/**
+ * pmksa_cache_auth_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Supplicant,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
+ aa, spa, session_timeout, eapol,
+ akmp);
+
+ if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
+ return NULL;
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function creates a PMKSA entry.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ if (pmk_len > PMK_LEN_MAX)
+ return NULL;
+
+ if (wpa_key_mgmt_suite_b(akmp) && !kck)
+ return NULL;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+ if (pmkid)
+ os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+ else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+ else if (wpa_key_mgmt_suite_b(akmp))
+ rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+ else
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
+ os_get_reltime(&now);
+ entry->expiration = now.sec;
+ if (session_timeout > 0)
+ entry->expiration += session_timeout;
+ else
+ entry->expiration += dot11RSNAConfigPMKLifetime;
+ entry->akmp = akmp;
+ os_memcpy(entry->spa, spa, ETH_ALEN);
+ pmksa_cache_from_eapol_data(entry, eapol);
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @entry: Pointer to PMKSA cache entry
+ *
+ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
+ * already in the cache for the same Supplicant, this entry will be replaced
+ * with the new entry. PMKID will be calculated based on the PMK.
+ */
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos;
+
+ if (entry == NULL)
+ return -1;
+
+ /* Replace an old entry for the same STA (if found) with the new entry
+ */
+ pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
+ if (pos)
+ pmksa_cache_free_entry(pmksa, pos);
+
+ if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+ "entry (for " MACSTR ") to make room for new one",
+ MAC2STR(pmksa->pmksa->spa));
+ pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+ }
+
+ pmksa_cache_link_entry(pmksa, entry);
+
+ return 0;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa, const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+ os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
+ entry->pmk_len = old_entry->pmk_len;
+ entry->expiration = old_entry->expiration;
+ entry->akmp = old_entry->akmp;
+ os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
+ entry->opportunistic = 1;
+ if (old_entry->identity) {
+ entry->identity = os_malloc(old_entry->identity_len);
+ if (entry->identity) {
+ entry->identity_len = old_entry->identity_len;
+ os_memcpy(entry->identity, old_entry->identity,
+ old_entry->identity_len);
+ }
+ }
+ if (old_entry->cui)
+ entry->cui = wpabuf_dup(old_entry->cui);
+#ifndef CONFIG_NO_RADIUS
+ radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+ entry->eap_type_authsrv = old_entry->eap_type_authsrv;
+ if (old_entry->vlan_desc) {
+ entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+ if (entry->vlan_desc)
+ *entry->vlan_desc = *old_entry->vlan_desc;
+ } else {
+ entry->vlan_desc = NULL;
+ }
+ entry->opportunistic = 1;
+
+ pmksa_cache_link_entry(pmksa, entry);
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev;
+ int i;
+
+ if (pmksa == NULL)
+ return;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ _pmksa_cache_free_entry(prev);
+ }
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ pmksa->pmksa_count = 0;
+ pmksa->pmksa = NULL;
+ for (i = 0; i < PMKID_HASH_SIZE; i++)
+ pmksa->pmkid[i] = NULL;
+ os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_auth_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @spa: Supplicant address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *spa, const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ if (pmkid) {
+ for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
+ entry = entry->hnext) {
+ if ((spa == NULL ||
+ os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+ return entry;
+ }
+ } else {
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (spa == NULL ||
+ os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: PMKID
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ *
+ * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+ struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+ const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ u8 new_pmkid[PMKID_LEN];
+
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+ continue;
+ if (wpa_key_mgmt_sae(entry->akmp) ||
+ wpa_key_mgmt_fils(entry->akmp)) {
+ if (os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+ return entry;
+ continue;
+ }
+ rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
+ entry->akmp);
+ if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
+ return entry;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_auth_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx), void *ctx)
+{
+ struct rsn_pmksa_cache *pmksa;
+
+ pmksa = os_zalloc(sizeof(*pmksa));
+ if (pmksa) {
+ pmksa->free_cb = free_cb;
+ pmksa->ctx = ctx;
+ }
+
+ return pmksa;
+}
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+ struct radius_das_attrs *attr)
+{
+ int match = 0;
+
+ if (attr->sta_addr) {
+ if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->acct_multi_session_id) {
+ char buf[20];
+
+ if (attr->acct_multi_session_id_len != 16)
+ return 0;
+ os_snprintf(buf, sizeof(buf), "%016llX",
+ (unsigned long long) entry->acct_multi_session_id);
+ if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->cui) {
+ if (!entry->cui ||
+ attr->cui_len != wpabuf_len(entry->cui) ||
+ os_memcmp(attr->cui, wpabuf_head(entry->cui),
+ attr->cui_len) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->user_name) {
+ if (!entry->identity ||
+ attr->user_name_len != entry->identity_len ||
+ os_memcmp(attr->user_name, entry->identity,
+ attr->user_name_len) != 0)
+ return 0;
+ match++;
+ }
+
+ return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr)
+{
+ int found = 0;
+ struct rsn_pmksa_cache_entry *entry, *prev;
+
+ if (attr->acct_session_id)
+ return -1;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if (das_attr_match(entry, attr)) {
+ found++;
+ prev = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, prev);
+ continue;
+ }
+ entry = entry->next;
+ }
+
+ return found ? 0 : -1;
+}
+
+
+/**
+ * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+ int i, ret;
+ char *pos = buf;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ ret = os_snprintf(pos, buf + len - pos,
+ "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ i = 0;
+ entry = pmksa->pmksa;
+ while (entry) {
+ ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+ i, MAC2STR(entry->spa));
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+ PMKID_LEN);
+ ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ (int) (entry->expiration - now.sec),
+ entry->opportunistic);
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+ entry = entry->next;
+ }
+ return pos - buf;
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+/**
+ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @addr: MAC address of the peer (NULL means any)
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
+ * in external storage.
+ */
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+ char *buf, size_t len)
+{
+ int ret;
+ char *pos, *end;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ pos = buf;
+ end = buf + len;
+ os_get_reltime(&now);
+
+
+ /*
+ * Entry format:
+ * <BSSID> <PMKID> <PMK> <expiration in seconds>
+ */
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+ continue;
+
+ ret = os_snprintf(pos, end - pos, MACSTR " ",
+ MAC2STR(entry->spa));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
+ PMKID_LEN);
+
+ ret = os_snprintf(pos, end - pos, " ");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
+ entry->pmk_len);
+
+ ret = os_snprintf(pos, end - pos, " %d\n",
+ (int) (entry->expiration - now.sec));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.h
new file mode 100644
index 0000000..e3cee4a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.h
@@ -0,0 +1,81 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+#include "radius/radius.h"
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+ struct rsn_pmksa_cache_entry *next, *hnext;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ os_time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 spa[ETH_ALEN];
+
+ u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
+ * public key */
+ u8 *identity;
+ size_t identity_len;
+ struct wpabuf *cui;
+ struct radius_class_data radius_class;
+ u8 eap_type_authsrv;
+ struct vlan_description *vlan_desc;
+ int opportunistic;
+
+ u64 acct_multi_session_id;
+};
+
+struct rsn_pmksa_cache;
+struct radius_das_attrs;
+
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx), void *ctx);
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *spa, const u8 *pmkid);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+ struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
+ const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp);
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa, const u8 *pmkid);
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+ struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol);
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+ char *buf, size_t len);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.c
new file mode 100644
index 0000000..3284a10
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.c
@@ -0,0 +1,273 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+
+#ifndef ETH_P_PREAUTH
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+#endif /* ETH_P_PREAUTH */
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_preauth_interface {
+ struct rsn_preauth_interface *next;
+ struct hostapd_data *hapd;
+ struct l2_packet_data *l2;
+ char *ifname;
+ int ifindex;
+};
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct rsn_preauth_interface *piface = ctx;
+ struct hostapd_data *hapd = piface->hapd;
+ struct ieee802_1x_hdr *hdr;
+ struct sta_info *sta;
+ struct l2_ethhdr *ethhdr;
+
+ wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
+ "from interface '%s'", piface->ifname);
+ if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
+ "(len=%lu)", (unsigned long) len);
+ return;
+ }
+
+ ethhdr = (struct l2_ethhdr *) buf;
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+ if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
+ MACSTR, MAC2STR(ethhdr->h_dest));
+ return;
+ }
+
+ sta = ap_get_sta(hapd, ethhdr->h_source);
+ if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
+ "STA " MACSTR, MAC2STR(sta->addr));
+ return;
+ }
+ if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+ sta = ap_sta_add(hapd, ethhdr->h_source);
+ if (sta == NULL)
+ return;
+ sta->flags = WLAN_STA_PREAUTH;
+
+ ieee802_1x_new_station(hapd, sta);
+ if (sta->eapol_sm == NULL) {
+ ap_free_sta(hapd, sta);
+ sta = NULL;
+ } else {
+ sta->eapol_sm->radius_identifier = -1;
+ sta->eapol_sm->portValid = true;
+ sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+ }
+ }
+ if (sta == NULL)
+ return;
+ sta->preauth_iface = piface;
+ ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+ len - sizeof(*ethhdr), FRAME_ENCRYPTION_UNKNOWN);
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+ struct rsn_preauth_interface *piface;
+
+ wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
+
+ piface = os_zalloc(sizeof(*piface));
+ if (piface == NULL)
+ return -1;
+ piface->hapd = hapd;
+
+ piface->ifname = os_strdup(ifname);
+ if (piface->ifname == NULL) {
+ goto fail1;
+ }
+
+ piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+ rsn_preauth_receive, piface, 1);
+ if (piface->l2 == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
+ "to ETH_P_PREAUTH");
+ goto fail2;
+ }
+
+ piface->next = hapd->preauth_iface;
+ hapd->preauth_iface = piface;
+ return 0;
+
+fail2:
+ os_free(piface->ifname);
+fail1:
+ os_free(piface);
+ return -1;
+}
+
+
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+ struct rsn_preauth_interface *piface, *prev;
+
+ piface = hapd->preauth_iface;
+ hapd->preauth_iface = NULL;
+ while (piface) {
+ prev = piface;
+ piface = piface->next;
+ l2_packet_deinit(prev->l2);
+ os_free(prev->ifname);
+ os_free(prev);
+ }
+}
+
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+ char *tmp, *start, *end;
+
+ if (hapd->conf->rsn_preauth_interfaces == NULL)
+ return 0;
+
+ tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
+ if (tmp == NULL)
+ return -1;
+ start = tmp;
+ for (;;) {
+ while (*start == ' ')
+ start++;
+ if (*start == '\0')
+ break;
+ end = os_strchr(start, ' ');
+ if (end)
+ *end = '\0';
+
+ if (rsn_preauth_iface_add(hapd, start)) {
+ rsn_preauth_iface_deinit(hapd);
+ os_free(tmp);
+ return -1;
+ }
+
+ if (end)
+ start = end + 1;
+ else
+ break;
+ }
+ os_free(tmp);
+ return 0;
+}
+
+
+static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
+ MACSTR, MAC2STR(sta->addr));
+ ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success)
+{
+ const u8 *key;
+ size_t len;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+ success ? "succeeded" : "failed");
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &len);
+ if (len > PMK_LEN)
+ len = PMK_LEN;
+ if (success && key) {
+ if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
+ sta->addr,
+ dot11RSNAConfigPMKLifetime,
+ sta->eapol_sm) == 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "added PMKSA cache entry (pre-auth)");
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "failed to add PMKSA cache entry "
+ "(pre-auth)");
+ }
+ }
+
+ /*
+ * Finish STA entry removal from timeout in order to avoid freeing
+ * STA data before the caller has finished processing.
+ */
+ eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+ struct rsn_preauth_interface *piface;
+ struct l2_ethhdr *ethhdr;
+
+ piface = hapd->preauth_iface;
+ while (piface) {
+ if (piface == sta->preauth_iface)
+ break;
+ piface = piface->next;
+ }
+
+ if (piface == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
+ "interface for " MACSTR, MAC2STR(sta->addr));
+ return;
+ }
+
+ ethhdr = os_malloc(sizeof(*ethhdr) + len);
+ if (ethhdr == NULL)
+ return;
+
+ os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+ os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+ ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
+ os_memcpy(ethhdr + 1, buf, len);
+
+ if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
+ sizeof(*ethhdr) + len) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
+ "l2_packet_send\n");
+ }
+ os_free(ethhdr);
+}
+
+
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.h
new file mode 100644
index 0000000..69fb356
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.h
@@ -0,0 +1,52 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+#ifdef CONFIG_RSN_PREAUTH
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd);
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success);
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len);
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_RSN_PREAUTH */
+
+static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void rsn_preauth_finished(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ int success)
+{
+}
+
+static inline void rsn_preauth_send(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+}
+
+static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
+
+#endif /* PREAUTH_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.c
new file mode 100644
index 0000000..8220590
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.c
@@ -0,0 +1,682 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "eloop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+
+#define HOSTAPD_RRM_REQUEST_TIMEOUT 5
+
+
+static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
+ hapd->lci_req_token);
+ hapd->lci_req_active = 0;
+}
+
+
+static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
+ const u8 *pos, size_t len)
+{
+ if (!hapd->lci_req_active || hapd->lci_req_token != token) {
+ wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
+ return;
+ }
+
+ hapd->lci_req_active = 0;
+ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
+}
+
+
+static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
+ hapd->range_req_token);
+ hapd->range_req_active = 0;
+}
+
+
+static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
+ const u8 *pos, size_t len)
+{
+ if (!hapd->range_req_active || hapd->range_req_token != token) {
+ wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
+ token);
+ return;
+ }
+
+ hapd->range_req_active = 0;
+ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
+}
+
+
+static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ const u8 *addr, u8 token, u8 rep_mode,
+ const u8 *pos, size_t len)
+{
+ char report[2 * 255 + 1];
+
+ wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
+ token, len, MAC2STR(addr));
+ /* Skip to the beginning of the Beacon report */
+ if (len < 3)
+ return;
+ pos += 3;
+ len -= 3;
+ report[0] = '\0';
+ if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ MAC2STR(addr), token, rep_mode, report);
+ if (len < sizeof(struct rrm_measurement_beacon_report))
+ return;
+ hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
+}
+
+
+static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ const u8 *pos, *ie, *end;
+ u8 token, rep_mode;
+
+ end = buf + len;
+ token = mgmt->u.action.u.rrm.dialog_token;
+ pos = mgmt->u.action.u.rrm.variable;
+
+ while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
+ if (ie[1] < 3) {
+ wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
+ break;
+ }
+
+ rep_mode = ie[3];
+ wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
+ rep_mode, ie[4]);
+
+ switch (ie[4]) {
+ case MEASURE_TYPE_LCI:
+ hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
+ break;
+ case MEASURE_TYPE_FTM_RANGE:
+ hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
+ break;
+ case MEASURE_TYPE_BEACON:
+ hostapd_handle_beacon_report(hapd, mgmt->sa, token,
+ rep_mode, ie + 2, ie[1]);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Measurement report type %u is not supported",
+ ie[4]);
+ break;
+ }
+
+ pos = ie + ie[1] + 2;
+ }
+}
+
+
+static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
+{
+ const u8 *subelem;
+
+ /* Range Request element + Location Subject + Maximum Age subelement */
+ if (len < 3 + 1 + 4)
+ return 0;
+
+ /* Subelements are arranged as IEs */
+ subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
+ if (subelem && subelem[1] == 2)
+ return WPA_GET_LE16(subelem + 2);
+
+ return 0;
+}
+
+
+static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
+{
+ struct os_time curr, diff;
+ unsigned long diff_l;
+
+ if (nr->stationary || max_age == 0xffff)
+ return 1;
+
+ if (!max_age)
+ return 0;
+
+ if (os_get_time(&curr))
+ return 0;
+
+ os_time_sub(&curr, &nr->lci_date, &diff);
+
+ /* avoid overflow */
+ if (diff.sec > 0xffff)
+ return 0;
+
+ /* LCI age is calculated in 10th of a second units. */
+ diff_l = diff.sec * 10 + diff.usec / 100000;
+
+ return max_age > diff_l;
+}
+
+
+static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
+ struct hostapd_neighbor_entry *nr,
+ int send_lci, int send_civic)
+{
+ size_t len = 2 + wpabuf_len(nr->nr);
+
+ if (send_lci && nr->lci)
+ len += 2 + wpabuf_len(nr->lci);
+
+ if (send_civic && nr->civic)
+ len += 2 + wpabuf_len(nr->civic);
+
+ return len;
+}
+
+
+static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+ const u8 *addr, u8 dialog_token,
+ struct wpa_ssid_value *ssid, u8 lci,
+ u8 civic, u16 lci_max_age)
+{
+ struct hostapd_neighbor_entry *nr;
+ struct wpabuf *buf;
+ u8 *msmt_token;
+
+ /*
+ * The number and length of the Neighbor Report elements in a Neighbor
+ * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
+ * of RRM header.
+ */
+ buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
+ if (!buf)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
+ wpabuf_put_u8(buf, dialog_token);
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ int send_lci;
+ size_t len;
+
+ if (ssid->ssid_len != nr->ssid.ssid_len ||
+ os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
+ continue;
+
+ send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
+ len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
+
+ if (len - 2 > 0xff) {
+ wpa_printf(MSG_DEBUG,
+ "NR entry for " MACSTR " exceeds 0xFF bytes",
+ MAC2STR(nr->bssid));
+ continue;
+ }
+
+ if (len > wpabuf_tailroom(buf))
+ break;
+
+ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+ wpabuf_put_u8(buf, len - 2);
+ wpabuf_put_buf(buf, nr->nr);
+
+ if (send_lci && nr->lci) {
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+ wpabuf_put_u8(buf, wpabuf_len(nr->lci));
+ /*
+ * Override measurement token - the first byte of the
+ * Measurement Report element.
+ */
+ msmt_token = wpabuf_put(buf, 0);
+ wpabuf_put_buf(buf, nr->lci);
+ *msmt_token = lci;
+ }
+
+ if (civic && nr->civic) {
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+ wpabuf_put_u8(buf, wpabuf_len(nr->civic));
+ /*
+ * Override measurement token - the first byte of the
+ * Measurement Report element.
+ */
+ msmt_token = wpabuf_put(buf, 0);
+ wpabuf_put_buf(buf, nr->civic);
+ *msmt_token = civic;
+ }
+ }
+
+ hapd->openwrt_stats.rrm.neighbor_report_tx++;
+
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+}
+
+
+static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ const u8 *pos, *ie, *end;
+ struct wpa_ssid_value ssid = {
+ .ssid_len = 0
+ };
+ u8 token;
+ u8 lci = 0, civic = 0; /* Measurement tokens */
+ u16 lci_max_age = 0;
+
+ if (!(hapd->conf->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+ return;
+
+ end = buf + len;
+
+ token = mgmt->u.action.u.rrm.dialog_token;
+ pos = mgmt->u.action.u.rrm.variable;
+ len = end - pos;
+
+ ie = get_ie(pos, len, WLAN_EID_SSID);
+ if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
+ ssid.ssid_len = ie[1];
+ os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
+ } else {
+ ssid.ssid_len = hapd->conf->ssid.ssid_len;
+ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+ }
+
+ while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
+ if (ie[1] < 3)
+ break;
+
+ wpa_printf(MSG_DEBUG,
+ "Neighbor report request, measure type %u",
+ ie[4]);
+
+ switch (ie[4]) { /* Measurement Type */
+ case MEASURE_TYPE_LCI:
+ lci = ie[2]; /* Measurement Token */
+ lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
+ ie[1]);
+ break;
+ case MEASURE_TYPE_LOCATION_CIVIC:
+ civic = ie[2]; /* Measurement token */
+ break;
+ }
+
+ pos = ie + ie[1] + 2;
+ len = end - pos;
+ }
+
+ hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
+ lci_max_age);
+}
+
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+
+ /*
+ * Check for enough bytes: header + (1B)Category + (1B)Action +
+ * (1B)Dialog Token.
+ */
+ if (len < IEEE80211_HDRLEN + 3)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
+ mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+
+ switch (mgmt->u.action.u.rrm.action) {
+ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+ hostapd_ubus_handle_link_measurement(hapd, buf, len);
+ break;
+ case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+ hostapd_handle_radio_msmt_report(hapd, buf, len);
+ break;
+ case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+ hostapd_handle_nei_report_req(hapd, buf, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
+ mgmt->u.action.u.rrm.action);
+ break;
+ }
+}
+
+
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ int ret;
+
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Request LCI: Destination address is not connected");
+ return -1;
+ }
+
+ if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
+ wpa_printf(MSG_INFO,
+ "Request LCI: Station does not support LCI in RRM");
+ return -1;
+ }
+
+ if (hapd->lci_req_active) {
+ wpa_printf(MSG_DEBUG,
+ "Request LCI: LCI request is already in process, overriding");
+ hapd->lci_req_active = 0;
+ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
+ NULL);
+ }
+
+ /* Measurement request (5) + Measurement element with LCI (10) */
+ buf = wpabuf_alloc(5 + 10);
+ if (!buf)
+ return -1;
+
+ hapd->lci_req_token++;
+ /* For wraparounds - the token must be nonzero */
+ if (!hapd->lci_req_token)
+ hapd->lci_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->lci_req_token);
+ wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ wpabuf_put_u8(buf, 3 + 1 + 4);
+
+ wpabuf_put_u8(buf, 1); /* Measurement Token */
+ /*
+ * Parallel and Enable bits are 0, Duration, Request, and Report are
+ * reserved.
+ */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
+
+ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+ wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+ wpabuf_put_u8(buf, 2);
+ wpabuf_put_le16(buf, 0xffff);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret)
+ return ret;
+
+ hapd->lci_req_active = 1;
+
+ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+ hostapd_lci_rep_timeout_handler, hapd, NULL);
+
+ return 0;
+}
+
+
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+ u16 random_interval, u8 min_ap,
+ const u8 *responders, unsigned int n_responders)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta;
+ u8 *len;
+ unsigned int i;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
+ " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
+ random_interval, min_ap, n_responders);
+
+ if (min_ap == 0 || min_ap > n_responders) {
+ wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Request range: Destination address is not connected");
+ return -1;
+ }
+
+ if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
+ wpa_printf(MSG_ERROR,
+ "Request range: Destination station does not support FTM range report in RRM");
+ return -1;
+ }
+
+ if (hapd->range_req_active) {
+ wpa_printf(MSG_DEBUG,
+ "Request range: Range request is already in process; overriding");
+ hapd->range_req_active = 0;
+ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
+ NULL);
+ }
+
+ /* Action + measurement type + token + reps + EID + len = 7 */
+ buf = wpabuf_alloc(7 + 255);
+ if (!buf)
+ return -1;
+
+ hapd->range_req_token++;
+ if (!hapd->range_req_token) /* For wraparounds */
+ hapd->range_req_token++;
+
+ /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
+ wpabuf_put_le16(buf, 0); /* Number of Repetitions */
+
+ /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ len = wpabuf_put(buf, 1); /* Length will be set later */
+
+ wpabuf_put_u8(buf, 1); /* Measurement Token */
+ /*
+ * Parallel and Enable bits are 0; Duration, Request, and Report are
+ * reserved.
+ */
+ wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+ wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
+
+ /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
+ wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
+ wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
+
+ /* FTM Range Subelements */
+
+ /*
+ * Taking the neighbor report part of the range request from neighbor
+ * database instead of requesting the separate bits of data from the
+ * user.
+ */
+ for (i = 0; i < n_responders; i++) {
+ struct hostapd_neighbor_entry *nr;
+
+ nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
+ NULL);
+ if (!nr) {
+ wpa_printf(MSG_INFO, "Missing neighbor report for "
+ MACSTR, MAC2STR(responders + ETH_ALEN * i));
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
+ wpa_printf(MSG_ERROR, "Too long range request");
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+ wpabuf_put_u8(buf, wpabuf_len(nr->nr));
+ wpabuf_put_buf(buf, nr->nr);
+ }
+
+ /* Action + measurement type + token + reps + EID + len = 7 */
+ *len = wpabuf_len(buf) - 7;
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret)
+ return ret;
+
+ hapd->range_req_active = 1;
+
+ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+ hostapd_range_rep_timeout_handler, hapd, NULL);
+
+ return 0;
+}
+
+
+void hostapd_clean_rrm(struct hostapd_data *hapd)
+{
+ hostapd_free_neighbor_db(hapd);
+ eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+ hapd->lci_req_active = 0;
+ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+ hapd->range_req_active = 0;
+}
+
+
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+ u8 req_mode, const struct wpabuf *req)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ int ret;
+ enum beacon_report_mode mode;
+ const u8 *pos;
+
+ /* Request data:
+ * Operating Class (1), Channel Number (1), Randomization Interval (2),
+ * Measurement Duration (2), Measurement Mode (1), BSSID (6),
+ * Optional Subelements (variable)
+ */
+ if (wpabuf_len(req) < 13) {
+ wpa_printf(MSG_INFO, "Beacon request: Too short request data");
+ return -1;
+ }
+ pos = wpabuf_head(req);
+ mode = pos[6];
+
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR " is not connected",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ switch (mode) {
+ case BEACON_REPORT_MODE_PASSIVE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support passive beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ case BEACON_REPORT_MODE_ACTIVE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support active beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ case BEACON_REPORT_MODE_TABLE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support table beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "Beacon request: Unknown measurement mode %d", mode);
+ return -1;
+ }
+
+ buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
+ if (!buf)
+ return -1;
+
+ hapd->beacon_req_token++;
+ if (!hapd->beacon_req_token)
+ hapd->beacon_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->beacon_req_token);
+ wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+ /* Measurement Request element */
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ wpabuf_put_u8(buf, 3 + wpabuf_len(req));
+ wpabuf_put_u8(buf, 1); /* Measurement Token */
+ wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
+ wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
+ wpabuf_put_buf(buf, req);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret < 0)
+ return ret;
+
+ return hapd->beacon_req_token;
+}
+
+
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ if (len < 24 + 3)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
+ " %u ack=%d", MAC2STR(mgmt->da),
+ mgmt->u.action.u.rrm.dialog_token, ok);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.h
new file mode 100644
index 0000000..02cd522
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.h
@@ -0,0 +1,33 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RRM_H
+#define RRM_H
+
+/*
+ * Max measure request length is 255, -6 of the body we have 249 for the
+ * neighbor report elements. Each neighbor report element is at least 2 + 13
+ * bytes, so we can't have more than 16 responders in the request.
+ */
+#define RRM_RANGE_REQ_MAX_RESPONDERS 16
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+ const u8 *buf, size_t len);
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+ u16 random_interval, u8 min_ap,
+ const u8 *responders, unsigned int n_responders);
+void hostapd_clean_rrm(struct hostapd_data *hapd);
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+ u8 req_mode, const struct wpabuf *req);
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok);
+
+#endif /* RRM_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.c
new file mode 100644
index 0000000..1843c1a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.c
@@ -0,0 +1,1678 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "common/dpp.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "crypto/crypto.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ap_mlme.h"
+#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_serv.h"
+#include "wnm_ap.h"
+#include "mbo_ap.h"
+#include "ndisc_snoop.h"
+#include "sta_info.h"
+#include "vlan.h"
+#include "wps_hostapd.h"
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+ struct sta_info *sta);
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (cb(hapd, sta, ctx))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+ struct sta_info *s;
+
+ s = hapd->sta_hash[STA_HASH(sta)];
+ while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
+ return s;
+}
+
+
+#ifdef CONFIG_P2P
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ const u8 *p2p_dev_addr;
+
+ if (sta->p2p_ie == NULL)
+ continue;
+
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ if (p2p_dev_addr == NULL)
+ continue;
+
+ if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+ return sta;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
+static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sta_info *tmp;
+
+ if (hapd->sta_list == sta) {
+ hapd->sta_list = sta->next;
+ return;
+ }
+
+ tmp = hapd->sta_list;
+ while (tmp != NULL && tmp->next != sta)
+ tmp = tmp->next;
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from "
+ "list.", MAC2STR(sta->addr));
+ } else
+ tmp->next = sta->next;
+}
+
+
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
+ hapd->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+
+static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sta_info *s;
+
+ s = hapd->sta_hash[STA_HASH(sta->addr)];
+ if (s == NULL) return;
+ if (os_memcmp(s->addr, sta->addr, 6) == 0) {
+ hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+ return;
+ }
+
+ while (s->hnext != NULL &&
+ os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+ s = s->hnext;
+ if (s->hnext != NULL)
+ s->hnext = s->hnext->hnext;
+ else
+ wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR
+ " from hash table", MAC2STR(sta->addr));
+}
+
+
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ sta_ip6addr_del(hapd, sta);
+}
+
+
+#ifdef CONFIG_PASN
+
+void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->pasn) {
+ wpa_printf(MSG_DEBUG, "PASN: Free PASN context: " MACSTR,
+ MAC2STR(sta->addr));
+
+ if (sta->pasn->ecdh)
+ crypto_ecdh_deinit(sta->pasn->ecdh);
+
+ wpabuf_free(sta->pasn->secret);
+ sta->pasn->secret = NULL;
+
+#ifdef CONFIG_SAE
+ sae_clear_data(&sta->pasn->sae);
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ /* In practice this pointer should be NULL */
+ wpabuf_free(sta->pasn->fils.erp_resp);
+ sta->pasn->fils.erp_resp = NULL;
+#endif /* CONFIG_FILS */
+
+ bin_clear_free(sta->pasn, sizeof(*sta->pasn));
+ sta->pasn = NULL;
+ }
+}
+
+#endif /* CONFIG_PASN */
+
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int set_beacon = 0;
+
+ accounting_sta_stop(hapd, sta);
+
+ /* just in case */
+ ap_sta_set_authorized(hapd, sta, 0);
+ hostapd_set_sta_flags(hapd, sta);
+
+ if ((sta->flags & WLAN_STA_WDS) ||
+ (sta->flags & WLAN_STA_MULTI_AP &&
+ !(hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+ !(sta->flags & WLAN_STA_WPS)))
+ hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
+
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
+
+ if (!hapd->iface->driver_ap_teardown &&
+ !(sta->flags & WLAN_STA_PREAUTH)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+
+ ap_sta_hash_del(hapd, sta);
+ ap_sta_list_del(hapd, sta);
+
+ if (sta->aid > 0)
+ hapd->sta_aid[(sta->aid - 1) / 32] &=
+ ~BIT((sta->aid - 1) % 32);
+
+ hapd->num_sta--;
+ if (sta->nonerp_set) {
+ sta->nonerp_set = 0;
+ hapd->iface->num_sta_non_erp--;
+ if (hapd->iface->num_sta_non_erp == 0)
+ set_beacon++;
+ }
+
+ if (sta->no_short_slot_time_set) {
+ sta->no_short_slot_time_set = 0;
+ hapd->iface->num_sta_no_short_slot_time--;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_slot_time == 0)
+ set_beacon++;
+ }
+
+ if (sta->no_short_preamble_set) {
+ sta->no_short_preamble_set = 0;
+ hapd->iface->num_sta_no_short_preamble--;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_preamble == 0)
+ set_beacon++;
+ }
+
+ if (sta->no_ht_gf_set) {
+ sta->no_ht_gf_set = 0;
+ hapd->iface->num_sta_ht_no_gf--;
+ }
+
+ if (sta->no_ht_set) {
+ sta->no_ht_set = 0;
+ hapd->iface->num_sta_no_ht--;
+ }
+
+ if (sta->ht_20mhz_set) {
+ sta->ht_20mhz_set = 0;
+ hapd->iface->num_sta_ht_20mhz--;
+ }
+
+#ifdef CONFIG_TAXONOMY
+ wpabuf_free(sta->probe_ie_taxonomy);
+ sta->probe_ie_taxonomy = NULL;
+ wpabuf_free(sta->assoc_ie_taxonomy);
+ sta->assoc_ie_taxonomy = NULL;
+#endif /* CONFIG_TAXONOMY */
+
+ ht40_intolerant_remove(hapd->iface, sta);
+
+#ifdef CONFIG_P2P
+ if (sta->no_p2p_set) {
+ sta->no_p2p_set = 0;
+ hapd->num_sta_no_p2p--;
+ if (hapd->num_sta_no_p2p == 0)
+ hostapd_p2p_non_p2p_sta_disconnected(hapd);
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef NEED_AP_MLME
+ if (hostapd_ht_operation_update(hapd->iface) > 0)
+ set_beacon++;
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+ if (hapd->mesh_sta_free_cb)
+ hapd->mesh_sta_free_cb(hapd, sta);
+#endif /* CONFIG_MESH */
+
+ if (set_beacon)
+ ieee802_11_set_beacons(hapd->iface);
+
+ wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
+ __func__, MAC2STR(sta->addr));
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+ ap_sta_clear_disconnect_timeouts(hapd, sta);
+ sae_clear_retransmit_timer(hapd, sta);
+
+ ieee802_1x_free_station(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
+ hapd->mld_link_id == sta->mld_assoc_link_id)
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#else
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
+ rsn_preauth_free_station(hapd, sta);
+#ifndef CONFIG_NO_RADIUS
+ if (hapd->radius)
+ radius_client_flush_auth(hapd->radius, sta->addr);
+#endif /* CONFIG_NO_RADIUS */
+
+#ifndef CONFIG_NO_VLAN
+ /*
+ * sta->wpa_sm->group needs to be released before so that
+ * vlan_remove_dynamic() can check that no stations are left on the
+ * AP_VLAN netdev.
+ */
+ if (sta->vlan_id)
+ vlan_remove_dynamic(hapd, sta->vlan_id);
+ if (sta->vlan_id_bound) {
+ /*
+ * Need to remove the STA entry before potentially removing the
+ * VLAN.
+ */
+ if (hapd->iface->driver_ap_teardown &&
+ !(sta->flags & WLAN_STA_PREAUTH)) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+ vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+ }
+#endif /* CONFIG_NO_VLAN */
+
+ os_free(sta->challenge);
+
+ os_free(sta->sa_query_trans_id);
+ eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+
+#ifdef CONFIG_P2P
+ p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+ if (sta->gas_dialog) {
+ int i;
+ for (i = 0; i < GAS_DIALOG_MAX; i++)
+ gas_serv_dialog_clear(&sta->gas_dialog[i]);
+ os_free(sta->gas_dialog);
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ wpabuf_free(sta->wps_ie);
+ wpabuf_free(sta->p2p_ie);
+ wpabuf_free(sta->hs20_ie);
+ wpabuf_free(sta->roaming_consortium);
+#ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+#endif /* CONFIG_FST */
+
+ os_free(sta->ht_capabilities);
+ os_free(sta->vht_capabilities);
+ os_free(sta->vht_operation);
+ os_free(sta->he_capab);
+ os_free(sta->he_6ghz_capab);
+ os_free(sta->eht_capab);
+ hostapd_free_psk_list(sta->psk);
+ os_free(sta->identity);
+ os_free(sta->radius_cui);
+ os_free(sta->remediation_url);
+ os_free(sta->t_c_url);
+ wpabuf_free(sta->hs20_deauth_req);
+ os_free(sta->hs20_session_info_url);
+
+#ifdef CONFIG_SAE
+ sae_clear_data(sta->sae);
+ os_free(sta->sae);
+#endif /* CONFIG_SAE */
+
+ mbo_ap_sta_free(sta);
+ os_free(sta->supp_op_classes);
+
+#ifdef CONFIG_FILS
+ os_free(sta->fils_pending_assoc_req);
+ wpabuf_free(sta->fils_hlp_resp);
+ wpabuf_free(sta->hlp_dhcp_discover);
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ wpabuf_free(sta->fils_g_sta);
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
+ crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+#endif /* CONFIG_DPP2 */
+
+ os_free(sta->ext_capability);
+
+#ifdef CONFIG_WNM_AP
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+#endif /* CONFIG_WNM_AP */
+
+#ifdef CONFIG_PASN
+ ap_free_sta_pasn(hapd, sta);
+#endif /* CONFIG_PASN */
+
+ os_free(sta->ifname_wds);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(sta->sae_postponed_commit);
+ forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_free(sta);
+}
+
+
+void hostapd_free_stas(struct hostapd_data *hapd)
+{
+ struct sta_info *sta, *prev;
+
+ sta = hapd->sta_list;
+
+ while (sta) {
+ prev = sta;
+ if (sta->flags & WLAN_STA_AUTH) {
+ mlme_deauthenticate_indication(
+ hapd, sta, WLAN_REASON_UNSPECIFIED);
+ }
+ sta = sta->next;
+ wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
+ MAC2STR(prev->addr));
+ ap_free_sta(hapd, prev);
+ }
+}
+
+
+/**
+ * ap_handle_timer - Per STA timer handler
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: struct sta_info *
+ *
+ * This function is called to check station activity and to remove inactive
+ * stations.
+ */
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ unsigned long next_time = 0;
+ int reason;
+
+ wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
+ hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
+ sta->timeout_next);
+ if (sta->timeout_next == STA_REMOVE) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "local deauth request");
+ hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
+ ap_free_sta(hapd, sta);
+ return;
+ }
+
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ (sta->timeout_next == STA_NULLFUNC ||
+ sta->timeout_next == STA_DISASSOC)) {
+ int inactive_sec;
+ /*
+ * Add random value to timeout so that we don't end up bouncing
+ * all stations at the same time if we have lots of associated
+ * stations that are idle (but keep re-associating).
+ */
+ int fuzz = os_random() % 20;
+ inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
+ if (inactive_sec == -1) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Check inactivity: Could not "
+ "get station info from kernel driver for "
+ MACSTR, MAC2STR(sta->addr));
+ /*
+ * The driver may not support this functionality.
+ * Anyway, try again after the next inactivity timeout,
+ * but do not disconnect the station now.
+ */
+ next_time = hapd->conf->ap_max_inactivity + fuzz;
+ } else if (inactive_sec == -ENOENT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has lost its driver entry",
+ MAC2STR(sta->addr));
+
+ /* Avoid sending client probe on removed client */
+ sta->timeout_next = STA_DISASSOC;
+ goto skip_poll;
+ } else if (inactive_sec < hapd->conf->ap_max_inactivity) {
+ /* station activity detected; reset timeout state */
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has been active %is ago",
+ MAC2STR(sta->addr), inactive_sec);
+ sta->timeout_next = STA_NULLFUNC;
+ next_time = hapd->conf->ap_max_inactivity + fuzz -
+ inactive_sec;
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has been "
+ "inactive too long: %d sec, max allowed: %d",
+ MAC2STR(sta->addr), inactive_sec,
+ hapd->conf->ap_max_inactivity);
+
+ if (hapd->conf->skip_inactivity_poll)
+ sta->timeout_next = STA_DISASSOC;
+ }
+ }
+
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ sta->timeout_next == STA_DISASSOC &&
+ !(sta->flags & WLAN_STA_PENDING_POLL) &&
+ !hapd->conf->skip_inactivity_poll) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " has ACKed data poll", MAC2STR(sta->addr));
+ /* data nullfunc frame poll did not produce TX errors; assume
+ * station ACKed it */
+ sta->timeout_next = STA_NULLFUNC;
+ next_time = hapd->conf->ap_max_inactivity;
+ }
+
+skip_poll:
+ if (next_time) {
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%lu seconds)",
+ __func__, MAC2STR(sta->addr), next_time);
+ eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
+ sta);
+ return;
+ }
+
+ if (sta->timeout_next == STA_NULLFUNC &&
+ (sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, " Polling STA");
+ sta->flags |= WLAN_STA_PENDING_POLL;
+ hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
+ sta->flags & WLAN_STA_WMM);
+ } else if (sta->timeout_next != STA_REMOVE) {
+ int deauth = sta->timeout_next == STA_DEAUTH;
+
+ if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) {
+ /* Cannot disassociate not-associated STA, so move
+ * directly to deauthentication. */
+ sta->timeout_next = STA_DEAUTH;
+ deauth = 1;
+ }
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Timeout, sending %s info to STA " MACSTR,
+ deauth ? "deauthentication" : "disassociation",
+ MAC2STR(sta->addr));
+
+ if (deauth) {
+ hostapd_drv_sta_deauth(
+ hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ } else {
+ reason = (sta->timeout_next == STA_DISASSOC) ?
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+ WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+ }
+ }
+
+ switch (sta->timeout_next) {
+ case STA_NULLFUNC:
+ sta->timeout_next = STA_DISASSOC;
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
+ __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
+ eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ break;
+ case STA_DISASSOC:
+ case STA_DISASSOC_FROM_CLI:
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~WLAN_STA_ASSOC;
+ hostapd_set_sta_flags(hapd, sta);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ if (!sta->acct_terminate_cause)
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(hapd, sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated due to "
+ "inactivity");
+ reason = (sta->timeout_next == STA_DISASSOC) ?
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+ WLAN_REASON_PREV_AUTH_NOT_VALID;
+ sta->timeout_next = STA_DEAUTH;
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
+ __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
+ eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ mlme_disassociate_indication(hapd, sta, reason);
+ break;
+ case STA_DEAUTH:
+ case STA_REMOVE:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "inactivity (timer DEAUTH/REMOVE)");
+ if (!sta->acct_terminate_cause)
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ mlme_deauthenticate_indication(
+ hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
+ ap_free_sta(hapd, sta);
+ break;
+ }
+}
+
+
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (!(sta->flags & (WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_AUTHORIZED))) {
+ if (sta->flags & WLAN_STA_GAS) {
+ wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+ "entry " MACSTR, MAC2STR(sta->addr));
+ ap_free_sta(hapd, sta);
+ }
+ return;
+ }
+
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ mlme_deauthenticate_indication(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "session timeout");
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
+ ap_free_sta(hapd, sta);
+}
+
+
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout)
+{
+ if (eloop_replenish_timeout(session_timeout, 0,
+ ap_handle_session_timer, hapd, sta) == 1) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "setting session timeout "
+ "to %d seconds", session_timeout);
+ }
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
+ "seconds", session_timeout);
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
+ hapd, sta);
+}
+
+
+void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+}
+
+
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+{
+#ifdef CONFIG_WNM_AP
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for "
+ MACSTR, hapd->conf->iface, MAC2STR(sta->addr));
+ if (sta->hs20_session_info_url == NULL)
+ return;
+
+ wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+ sta->hs20_disassoc_timer);
+#endif /* CONFIG_WNM_AP */
+}
+
+
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time)
+{
+ eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+ eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer,
+ hapd, sta);
+}
+
+
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+ int i;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ return sta;
+
+ wpa_printf(MSG_DEBUG, " New STA");
+ //if (hapd->num_sta >= hapd->conf->max_num_sta) {
+ if (hostapd_check_max_sta(hapd)) {
+ /* FIX: might try to remove some old STAs first? */
+ wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
+ hapd->num_sta, hapd->conf->max_num_sta);
+ return NULL;
+ }
+
+ sta = os_zalloc(sizeof(struct sta_info));
+ if (sta == NULL) {
+ wpa_printf(MSG_ERROR, "malloc failed");
+ return NULL;
+ }
+ sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+ if (accounting_sta_get_id(hapd, sta) < 0) {
+ os_free(sta);
+ return NULL;
+ }
+
+ for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+ if (!hapd->iface->basic_rates)
+ break;
+ if (hapd->iface->basic_rates[i] < 0)
+ break;
+ sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5;
+ }
+ sta->supported_rates_len = i;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ __func__, MAC2STR(addr),
+ hapd->conf->ap_max_inactivity);
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ ap_handle_timer, hapd, sta);
+ }
+
+ /* initialize STA info data */
+ os_memcpy(sta->addr, addr, ETH_ALEN);
+ sta->next = hapd->sta_list;
+ hapd->sta_list = sta;
+ hapd->num_sta++;
+ ap_sta_hash_add(hapd, sta);
+ ap_sta_remove_in_other_bss(hapd, sta);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ dl_list_init(&sta->ip6addr);
+
+#ifdef CONFIG_TAXONOMY
+ sta_track_claim_taxonomy_info(hapd->iface, addr,
+ &sta->probe_ie_taxonomy);
+#endif /* CONFIG_TAXONOMY */
+
+ return sta;
+}
+
+
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
+
+ wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver",
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (hostapd_drv_sta_remove(hapd, sta->addr) &&
+ sta->flags & WLAN_STA_ASSOC) {
+ wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR
+ " from kernel driver",
+ hapd->conf->iface, MAC2STR(sta->addr));
+ return -1;
+ }
+ sta->added_unassoc = 0;
+ return 0;
+}
+
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+ struct sta_info *sta2;
+ /* bss should always be set during operation, but it may be
+ * NULL during reconfiguration. Assume the STA is not
+ * associated to another BSS in that case to avoid NULL pointer
+ * dereferences. */
+ if (bss == hapd || bss == NULL)
+ continue;
+ sta2 = ap_get_sta(bss, sta->addr);
+ if (!sta2)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR
+ " association from another BSS %s",
+ hapd->conf->iface, MAC2STR(sta2->addr),
+ bss->conf->iface);
+ ap_sta_disconnect(bss, sta2, sta2->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+}
+
+
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ ap_sta_remove(hapd, sta);
+ mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Skip deauthentication in DMG/IEEE 802.11ad */
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_REMOVE;
+ } else {
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_DEAUTH;
+ }
+ ap_sta_set_authorized(hapd, sta, 0);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+ __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DISASSOC);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+ ap_handle_timer, hapd, sta);
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(hapd, sta);
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap ||
+ hapd->mld_link_id == sta->mld_assoc_link_id)
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#else
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
+ sta->wpa_sm = NULL;
+
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ ap_sta_remove(hapd, sta);
+ mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ ap_sta_disassociate(hapd, sta, reason);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ ap_sta_set_authorized(hapd, sta, 0);
+ hostapd_set_sta_flags(hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+ __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DEAUTH);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ ap_handle_timer, hapd, sta);
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(hapd, sta);
+
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (sta && (sta->flags & WLAN_STA_WPS)) {
+ ap_sta_deauthenticate(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+ __func__, MAC2STR(sta->addr));
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_WPS */
+
+
+static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd)
+{
+ struct hostapd_vlan *vlan;
+ int vlan_id = MAX_VLAN_ID + 2;
+
+retry:
+ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ if (vlan->vlan_id == vlan_id) {
+ vlan_id++;
+ goto retry;
+ }
+ }
+ return vlan_id;
+}
+
+
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ struct vlan_description *vlan_desc)
+{
+ struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
+ int old_vlan_id, vlan_id = 0, ret = 0;
+
+ /* Check if there is something to do */
+ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
+ /* This sta is lacking its own vif */
+ } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED &&
+ !hapd->conf->ssid.per_sta_vif && sta->vlan_id) {
+ /* sta->vlan_id needs to be reset */
+ } else if (!vlan_compare(vlan_desc, sta->vlan_desc)) {
+ return 0; /* nothing to change */
+ }
+
+ /* Now the real VLAN changed or the STA just needs its own vif */
+ if (hapd->conf->ssid.per_sta_vif) {
+ /* Assign a new vif, always */
+ /* find a free vlan_id sufficiently big */
+ vlan_id = ap_sta_get_free_vlan_id(hapd);
+ /* Get wildcard VLAN */
+ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ if (vlan->vlan_id == VLAN_ID_WILDCARD)
+ break;
+ }
+ if (!vlan) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "per_sta_vif missing wildcard");
+ vlan_id = 0;
+ ret = -1;
+ goto done;
+ }
+ } else if (vlan_desc && vlan_desc->notempty) {
+ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
+ break;
+ if (vlan->vlan_id == VLAN_ID_WILDCARD)
+ wildcard_vlan = vlan;
+ }
+ if (vlan) {
+ vlan_id = vlan->vlan_id;
+ } else if (wildcard_vlan) {
+ vlan = wildcard_vlan;
+ vlan_id = vlan_desc->untagged;
+ if (vlan_desc->tagged[0]) {
+ /* Tagged VLAN configuration */
+ vlan_id = ap_sta_get_free_vlan_id(hapd);
+ }
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "missing vlan and wildcard for vlan=%d%s",
+ vlan_desc->untagged,
+ vlan_desc->tagged[0] ? "+" : "");
+ vlan_id = 0;
+ ret = -1;
+ goto done;
+ }
+ }
+
+ if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) {
+ vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc);
+ if (vlan == NULL) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "could not add dynamic VLAN interface for vlan=%d%s",
+ vlan_desc ? vlan_desc->untagged : -1,
+ (vlan_desc && vlan_desc->tagged[0]) ?
+ "+" : "");
+ vlan_id = 0;
+ ret = -1;
+ goto done;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "added new dynamic VLAN interface '%s'",
+ vlan->ifname);
+ } else if (vlan && vlan->dynamic_vlan > 0) {
+ vlan->dynamic_vlan++;
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "updated existing dynamic VLAN interface '%s'",
+ vlan->ifname);
+ }
+done:
+ old_vlan_id = sta->vlan_id;
+ sta->vlan_id = vlan_id;
+ sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL;
+
+ if (vlan_id != old_vlan_id && old_vlan_id)
+ vlan_remove_dynamic(hapd, old_vlan_id);
+
+ return ret;
+}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifndef CONFIG_NO_VLAN
+ const char *iface;
+ struct hostapd_vlan *vlan = NULL;
+ int ret;
+ int old_vlanid = sta->vlan_id_bound;
+ int mld_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ mld_link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) {
+ wpa_printf(MSG_DEBUG,
+ "Do not override WDS VLAN assignment for STA "
+ MACSTR, MAC2STR(sta->addr));
+ return 0;
+ }
+
+ iface = hapd->conf->iface;
+ if (hapd->conf->ssid.vlan[0])
+ iface = hapd->conf->ssid.vlan;
+
+ if (sta->vlan_id > 0) {
+ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ if (vlan->vlan_id == sta->vlan_id)
+ break;
+ }
+ if (vlan)
+ iface = vlan->ifname;
+ }
+
+ /*
+ * Do not increment ref counters if the VLAN ID remains same, but do
+ * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might
+ * have been called before.
+ */
+ if (sta->vlan_id == old_vlanid)
+ goto skip_counting;
+
+ if (sta->vlan_id > 0 && !vlan &&
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
+ "binding station to (vlan_id=%d)",
+ sta->vlan_id);
+ ret = -1;
+ goto done;
+ } else if (vlan && vlan->dynamic_vlan > 0) {
+ vlan->dynamic_vlan++;
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "updated existing dynamic VLAN interface '%s'",
+ iface);
+ }
+
+ /* ref counters have been increased, so mark the station */
+ sta->vlan_id_bound = sta->vlan_id;
+
+skip_counting:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "binding station to interface "
+ "'%s'", iface);
+
+ if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
+ wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
+
+ ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id,
+ mld_link_id);
+ if (ret < 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
+ "entry to vlan_id=%d", sta->vlan_id);
+ }
+
+ /* During 1x reauth, if the vlan id changes, then remove the old id. */
+ if (old_vlanid > 0 && old_vlanid != sta->vlan_id)
+ vlan_remove_dynamic(hapd, old_vlanid);
+done:
+
+ return ret;
+#else /* CONFIG_NO_VLAN */
+ return 0;
+#endif /* CONFIG_NO_VLAN */
+}
+
+
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ u32 tu;
+ struct os_reltime now, passed;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &sta->sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (hapd->conf->assoc_sa_query_max_timeout < tu) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "association SA Query timed out");
+ sta->sa_query_timed_out = 1;
+ os_free(sta->sa_query_trans_id);
+ sta->sa_query_trans_id = NULL;
+ sta->sa_query_count = 0;
+ eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ unsigned int timeout, sec, usec;
+ u8 *trans_id, *nbuf;
+
+ wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR
+ " (count=%d)",
+ hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count);
+
+ if (sta->sa_query_count > 0 &&
+ ap_check_sa_query_timeout(hapd, sta))
+ return;
+ if (sta->sa_query_count >= 1000)
+ return;
+
+ nbuf = os_realloc_array(sta->sa_query_trans_id,
+ sta->sa_query_count + 1,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ if (nbuf == NULL)
+ return;
+ if (sta->sa_query_count == 0) {
+ /* Starting a new SA Query procedure */
+ os_get_reltime(&sta->sa_query_start);
+ }
+ trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+ sta->sa_query_trans_id = nbuf;
+ sta->sa_query_count++;
+
+ if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+ /*
+ * We don't really care which ID is used here, so simply
+ * hardcode this if the mostly theoretical os_get_random()
+ * failure happens.
+ */
+ trans_id[0] = 0x12;
+ trans_id[1] = 0x34;
+ }
+
+ timeout = hapd->conf->assoc_sa_query_retry_timeout;
+ sec = ((timeout / 1000) * 1024) / 1000;
+ usec = (timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "association SA Query attempt %d", sta->sa_query_count);
+
+ ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
+}
+
+
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ ap_sa_query_timer(hapd, sta);
+}
+
+
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+ os_free(sta->sa_query_trans_id);
+ sta->sa_query_trans_id = NULL;
+ sta->sa_query_count = 0;
+}
+
+
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_wpa_psk *psk;
+ struct hostapd_ssid *ssid;
+ const u8 *pmk;
+ int pmk_len;
+
+ ssid = &hapd->conf->ssid;
+
+ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+ if (!pmk || pmk_len != PMK_LEN)
+ return NULL;
+
+ for (psk = ssid->wpa_psk; psk; psk = psk->next)
+ if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
+ break;
+ if (!psk || !psk->keyid[0])
+ return NULL;
+
+ return psk->keyid;
+}
+
+
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ const u8 *dev_addr = NULL;
+ char buf[100];
+#ifdef CONFIG_P2P
+ u8 addr[ETH_ALEN];
+ u8 ip_addr_buf[4];
+#endif /* CONFIG_P2P */
+ struct hostapd_ubus_request req;
+
+ if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+ return;
+
+ os_memset(&req, 0, sizeof(req));
+ if (authorized) {
+ int mld_assoc_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (sta->mld_assoc_link_id == hapd->mld_link_id)
+ mld_assoc_link_id = sta->mld_assoc_link_id;
+ else
+ mld_assoc_link_id = -2;
+ }
+#endif /* CONFIG_IEEE80211BE */
+ if (mld_assoc_link_id != -2)
+ hostapd_prune_associations(hapd, sta->addr,
+ mld_assoc_link_id);
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ req.type = HOSTAPD_UBUS_CONNECTED_REQ;
+ req.addr = sta->addr;
+ } else {
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+ req.type = HOSTAPD_UBUS_DISCONNECTED_REQ;
+ req.addr = sta->addr;
+ }
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p_group == NULL) {
+ if (sta->p2p_ie != NULL &&
+ p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
+ dev_addr = addr;
+ } else
+ dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+
+ if (dev_addr)
+ os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else
+#endif /* CONFIG_P2P */
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+
+ if (hapd->sta_authorized_cb)
+ hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+ sta->addr, authorized, dev_addr);
+
+ if (authorized) {
+ static const char * const auth_algs[] = {
+ [WLAN_AUTH_OPEN] = "open",
+ [WLAN_AUTH_SHARED_KEY] = "shared",
+ [WLAN_AUTH_FT] = "ft",
+ [WLAN_AUTH_SAE] = "sae",
+ [WLAN_AUTH_FILS_SK] = "fils-sk",
+ [WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
+ [WLAN_AUTH_FILS_PK] = "fils-pk",
+ [WLAN_AUTH_PASN] = "pasn",
+ };
+ const char *auth_alg = NULL;
+ const u8 *dpp_pkhash;
+ const char *keyid;
+ char dpp_pkhash_buf[100];
+ char keyid_buf[100];
+ char ip_addr[100];
+ char alg_buf[100];
+
+ dpp_pkhash_buf[0] = '\0';
+ keyid_buf[0] = '\0';
+ ip_addr[0] = '\0';
+ alg_buf[0] = '\0';
+#ifdef CONFIG_P2P
+ if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ os_snprintf(ip_addr, sizeof(ip_addr),
+ " ip_addr=%u.%u.%u.%u",
+ ip_addr_buf[0], ip_addr_buf[1],
+ ip_addr_buf[2], ip_addr_buf[3]);
+ }
+#endif /* CONFIG_P2P */
+
+ if (sta->auth_alg < ARRAY_SIZE(auth_algs))
+ auth_alg = auth_algs[sta->auth_alg];
+
+ if (auth_alg)
+ os_snprintf(alg_buf, sizeof(alg_buf),
+ " auth_alg=%s", auth_alg);
+
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ os_snprintf(keyid_buf, sizeof(keyid_buf),
+ " keyid=%s", keyid);
+ }
+
+ dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+ if (dpp_pkhash) {
+ const char *prefix = " dpp_pkhash=";
+ size_t plen = os_strlen(prefix);
+
+ os_strlcpy(dpp_pkhash_buf, prefix,
+ sizeof(dpp_pkhash_buf));
+ wpa_snprintf_hex(&dpp_pkhash_buf[plen],
+ sizeof(dpp_pkhash_buf) - plen,
+ dpp_pkhash, SHA256_MAC_LEN);
+ }
+
+ hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
+ buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
+
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_CONNECTED "%s%s%s%s%s",
+ buf, ip_addr, keyid_buf,
+ dpp_pkhash_buf, alg_buf);
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
+ hostapd_ubus_notify(hapd, "disassoc", sta->addr);
+
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_DISCONNECTED "%s", buf);
+ }
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst) {
+ if (authorized)
+ fst_notify_peer_connected(hapd->iface->fst, sta->addr);
+ else
+ fst_notify_peer_disconnected(hapd->iface->fst,
+ sta->addr);
+ }
+#endif /* CONFIG_FST */
+
+ hostapd_ubus_handle_event(hapd, &req);
+}
+
+
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 reason)
+{
+ if (sta)
+ wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u",
+ hapd->conf->iface, __func__, MAC2STR(sta->addr),
+ reason);
+ else if (addr)
+ wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u",
+ hapd->conf->iface, __func__, MAC2STR(addr),
+ reason);
+
+ if (sta == NULL && addr)
+ sta = ap_get_sta(hapd, addr);
+
+ if (addr)
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+
+ if (sta == NULL)
+ return;
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+ hapd->conf->iface, __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DEAUTH);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ ap_handle_timer, hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
+ 2 : 0, 0, ap_sta_disassoc_cb_timeout,
+ hapd, sta);
+ return;
+ }
+
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) {
+ wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame");
+ return;
+ }
+ sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ ap_sta_deauth_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) {
+ wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame");
+ return;
+ }
+ sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ ap_sta_disassoc_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0)
+ wpa_printf(MSG_DEBUG,
+ "%s: Removed ap_sta_deauth_cb_timeout timeout for "
+ MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0)
+ wpa_printf(MSG_DEBUG,
+ "%s: Removed ap_sta_disassoc_cb_timeout timeout for "
+ MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for "
+ MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (sta->flags & WLAN_STA_WPS)
+ hostapd_wps_eap_completed(hapd);
+ }
+}
+
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+{
+ int res;
+
+ buf[0] = '\0';
+ res = os_snprintf(buf, buflen,
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+ (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+ (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
+ (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
+ ""),
+ (flags & WLAN_STA_SHORT_PREAMBLE ?
+ "[SHORT_PREAMBLE]" : ""),
+ (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
+ (flags & WLAN_STA_WMM ? "[WMM]" : ""),
+ (flags & WLAN_STA_MFP ? "[MFP]" : ""),
+ (flags & WLAN_STA_WPS ? "[WPS]" : ""),
+ (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
+ (flags & WLAN_STA_WDS ? "[WDS]" : ""),
+ (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+ (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
+ (flags & WLAN_STA_GAS ? "[GAS]" : ""),
+ (flags & WLAN_STA_HT ? "[HT]" : ""),
+ (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+ (flags & WLAN_STA_HE ? "[HE]" : ""),
+ (flags & WLAN_STA_EHT ? "[EHT]" : ""),
+ (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
+ (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+ (flags & WLAN_STA_WNM_SLEEP_MODE ?
+ "[WNM_SLEEP_MODE]" : ""));
+ if (os_snprintf_error(buflen, res))
+ res = -1;
+
+ return res;
+}
+
+
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ u16 reason;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X: Scheduled disconnection of " MACSTR
+ " after EAP-Failure", MAC2STR(sta->addr));
+
+ reason = sta->disconnect_reason_code;
+ if (!reason)
+ reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
+ ap_sta_disconnect(hapd, sta, sta->addr, reason);
+ if (sta->flags & WLAN_STA_WPS)
+ hostapd_wps_eap_completed(hapd);
+}
+
+
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ unsigned timeout)
+{
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X: Force disconnection of " MACSTR
+ " after EAP-Failure in %u ms", MAC2STR(sta->addr), timeout);
+
+ /*
+ * Add a small sleep to increase likelihood of previously requested
+ * EAP-Failure TX getting out before this should the driver reorder
+ * operations.
+ */
+ eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+ eloop_register_timeout(0, timeout * 1000,
+ ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+}
+
+
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb,
+ hapd, sta);
+}
+
+
+int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ const u8 *mld_link_addr = NULL;
+ bool mld_link_sta = false;
+
+ /*
+ * If a station that is already associated to the AP, is trying to
+ * authenticate again, remove the STA entry, in order to make sure the
+ * STA PS state gets cleared and configuration gets updated. To handle
+ * this, station's added_unassoc flag is cleared once the station has
+ * completed association.
+ */
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ u8 mld_link_id = hapd->mld_link_id;
+
+ mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
+ mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ ap_sta_set_authorized(hapd, sta, 0);
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
+
+ if (hostapd_sta_add(hapd, sta->addr, 0, 0,
+ sta->supported_rates,
+ sta->supported_rates_len,
+ 0, NULL, NULL, NULL, 0, NULL, 0, NULL,
+ sta->flags, 0, 0, 0, 0,
+ mld_link_addr, mld_link_sta)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "Could not add STA to kernel driver");
+ return -1;
+ }
+
+ sta->added_unassoc = 1;
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.h
new file mode 100644
index 0000000..e6b6881
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.h
@@ -0,0 +1,419 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+#include "common/defs.h"
+#include "list.h"
+#include "vlan.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "crypto/sha384.h"
+#include "pasn/pasn_common.h"
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_GAS BIT(17)
+#define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT(22)
+#define WLAN_STA_MULTI_AP BIT(23)
+#define WLAN_STA_HE BIT(24)
+#define WLAN_STA_6GHZ BIT(25)
+#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
+#define WLAN_STA_EHT BIT(27)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+#define WLAN_STA_NONERP BIT(31)
+
+/* Maximum number of supported rates (from both Supported Rates and Extended
+ * Supported Rates IEs). */
+#define WLAN_SUPP_RATES_MAX 32
+
+struct hostapd_data;
+
+struct mbo_non_pref_chan_info {
+ struct mbo_non_pref_chan_info *next;
+ u8 op_class;
+ u8 pref;
+ u8 reason_code;
+ u8 num_channels;
+ u8 channels[];
+};
+
+struct pending_eapol_rx {
+ struct wpabuf *buf;
+ struct os_reltime rx_time;
+ enum frame_encryption encrypted;
+};
+
+#define EHT_ML_MAX_STA_PROF_LEN 1024
+struct mld_info {
+ bool mld_sta;
+
+ struct ml_common_info {
+ u8 mld_addr[ETH_ALEN];
+ u16 medium_sync_delay;
+ u16 eml_capa;
+ u16 mld_capa;
+ } common_info;
+
+ struct mld_link_info {
+ u8 valid;
+ u8 local_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+
+ size_t nstr_bitmap_len;
+ u8 nstr_bitmap[2];
+
+ u16 capability;
+
+ u16 status;
+ size_t resp_sta_profile_len;
+ u8 resp_sta_profile[EHT_ML_MAX_STA_PROF_LEN];
+
+ const u8 *rsne, *rsnxe;
+ } links[MAX_NUM_MLD_LINKS];
+};
+
+struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
+ be32 ipaddr;
+ struct dl_list ip6addr; /* list head for struct ip6addr */
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u16 disconnect_reason_code; /* RADIUS server override */
+ u32 flags; /* Bitfield of WLAN_STA_* */
+ u16 capability;
+ u16 listen_interval; /* or beacon_int for APs */
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+ int supported_rates_len;
+ u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+
+#ifdef CONFIG_MESH
+ enum mesh_plink_state plink_state;
+ u16 peer_lid;
+ u16 my_lid;
+ u16 peer_aid;
+ u16 mpm_close_reason;
+ int mpm_retries;
+ u8 my_nonce[WPA_NONCE_LEN];
+ u8 peer_nonce[WPA_NONCE_LEN];
+ u8 aek[32]; /* SHA256 digest length */
+ u8 mtk[WPA_TK_MAX_LEN];
+ size_t mtk_len;
+ u8 mgtk_rsc[6];
+ u8 mgtk_key_id;
+ u8 mgtk[WPA_TK_MAX_LEN];
+ size_t mgtk_len;
+ u8 igtk_rsc[6];
+ u8 igtk[WPA_TK_MAX_LEN];
+ size_t igtk_len;
+ u16 igtk_key_id;
+ u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
+ unsigned int nonerp_set:1;
+ unsigned int no_short_slot_time_set:1;
+ unsigned int no_short_preamble_set:1;
+ unsigned int no_ht_gf_set:1;
+ unsigned int no_ht_set:1;
+ unsigned int ht40_intolerant_set:1;
+ unsigned int ht_20mhz_set:1;
+ unsigned int no_p2p_set:1;
+ unsigned int qos_map_enabled:1;
+ unsigned int remediation:1;
+ unsigned int hs20_deauth_requested:1;
+ unsigned int hs20_deauth_on_ack:1;
+ unsigned int session_timeout_set:1;
+ unsigned int radius_das_match:1;
+ unsigned int ecsa_supported:1;
+ unsigned int added_unassoc:1;
+ unsigned int pending_wds_enable:1;
+ unsigned int power_capab:1;
+ unsigned int agreed_to_steer:1;
+ unsigned int hs20_t_c_filtering:1;
+ unsigned int ft_over_ds:1;
+ unsigned int external_dh_updated:1;
+ unsigned int post_csa_sa_query:1;
+
+ u16 auth_alg;
+
+ enum {
+ STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
+ STA_DISASSOC_FROM_CLI
+ } timeout_next;
+
+ u16 deauth_reason;
+ u16 disassoc_reason;
+
+ /* IEEE 802.1X related data */
+ struct eapol_state_machine *eapol_sm;
+
+ struct pending_eapol_rx *pending_eapol_rx;
+
+ u64 acct_session_id;
+ struct os_reltime acct_session_start;
+ int acct_session_started;
+ int acct_terminate_cause; /* Acct-Terminate-Cause */
+ int acct_interim_interval; /* Acct-Interim-Interval */
+ unsigned int acct_interim_errors;
+
+ /* For extending 32-bit driver counters to 64-bit counters */
+ u32 last_rx_bytes_hi;
+ u32 last_rx_bytes_lo;
+ u32 last_tx_bytes_hi;
+ u32 last_tx_bytes_lo;
+
+ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+ struct wpa_state_machine *wpa_sm;
+ struct rsn_preauth_interface *preauth_iface;
+
+ int vlan_id; /* 0: none, >0: VID */
+ struct vlan_description *vlan_desc;
+ int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
+ /* PSKs from RADIUS authentication server */
+ struct hostapd_sta_wpa_psk_short *psk;
+
+ char *identity; /* User-Name from RADIUS */
+ char *radius_cui; /* Chargeable-User-Identity from RADIUS */
+
+ struct ieee80211_ht_capabilities *ht_capabilities;
+ struct ieee80211_vht_capabilities *vht_capabilities;
+ struct ieee80211_vht_operation *vht_operation;
+ u8 vht_opmode;
+ struct ieee80211_he_capabilities *he_capab;
+ size_t he_capab_len;
+ struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+ struct ieee80211_eht_capabilities *eht_capab;
+ size_t eht_capab_len;
+
+ int sa_query_count; /* number of pending SA Query requests;
+ * 0 = no SA Query in progress */
+ int sa_query_timed_out;
+ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+ * sa_query_count octets of pending SA Query
+ * transaction identifiers */
+ struct os_reltime sa_query_start;
+
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+ struct gas_dialog_info *gas_dialog;
+ u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
+
+ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
+ struct wpabuf *roaming_consortium;
+ u8 remediation_method;
+ char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
+ struct wpabuf *hs20_deauth_req;
+ char *hs20_session_info_url;
+ int hs20_disassoc_timer;
+#ifdef CONFIG_FST
+ struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
+#endif /* CONFIG_FST */
+
+ struct os_reltime connected_time;
+
+#ifdef CONFIG_SAE
+ struct sae_data *sae;
+ unsigned int mesh_sae_pmksa_caching:1;
+#endif /* CONFIG_SAE */
+
+ /* valid only if session_timeout_set == 1 */
+ struct os_reltime session_timeout;
+
+ /* Last Authentication/(Re)Association Request/Action frame sequence
+ * control */
+ u16 last_seq_ctrl;
+ /* Last Authentication/(Re)Association Request/Action frame subtype */
+ u8 last_subtype;
+
+#ifdef CONFIG_MBO
+ u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
+ * enum mbo_cellular_capa values */
+ struct mbo_non_pref_chan_info *non_pref_chan;
+ int auth_rssi; /* Last Authentication frame RSSI */
+#endif /* CONFIG_MBO */
+
+ u8 *supp_op_classes; /* Supported Operating Classes element, if
+ * received, starting from the Length field */
+
+ u8 rrm_enabled_capa[5];
+
+ s8 min_tx_power;
+ s8 max_tx_power;
+
+#ifdef CONFIG_TAXONOMY
+ struct wpabuf *probe_ie_taxonomy;
+ struct wpabuf *assoc_ie_taxonomy;
+#endif /* CONFIG_TAXONOMY */
+
+#ifdef CONFIG_FILS
+ u8 fils_snonce[FILS_NONCE_LEN];
+ u8 fils_session[FILS_SESSION_LEN];
+ u8 fils_erp_pmkid[PMKID_LEN];
+ u8 *fils_pending_assoc_req;
+ size_t fils_pending_assoc_req_len;
+ unsigned int fils_pending_assoc_is_reassoc:1;
+ unsigned int fils_dhcp_rapid_commit_proxy:1;
+ unsigned int fils_erp_pmkid_set:1;
+ unsigned int fils_drv_assoc_finish:1;
+ struct wpabuf *fils_hlp_resp;
+ struct wpabuf *hlp_dhcp_discover;
+ void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 resp, struct wpabuf *data, int pub);
+#ifdef CONFIG_FILS_SK_PFS
+ struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+ struct wpabuf *fils_dh_ss;
+ struct wpabuf *fils_g_sta;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ u8 *owe_pmk;
+ size_t owe_pmk_len;
+ struct crypto_ecdh *owe_ecdh;
+ u16 owe_group;
+#endif /* CONFIG_OWE */
+
+ u8 *ext_capability;
+ char *ifname_wds; /* WDS ifname, if in use */
+
+#ifdef CONFIG_DPP2
+ struct dpp_pfs *dpp_pfs;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ enum wpa_alg last_tk_alg;
+ int last_tk_key_idx;
+ u8 last_tk[WPA_TK_MAX_LEN];
+ size_t last_tk_len;
+ u8 *sae_postponed_commit;
+ size_t sae_postponed_commit_len;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_AIRTIME_POLICY
+ unsigned int airtime_weight;
+ unsigned int dyn_airtime_weight;
+ struct os_reltime backlogged_until;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_PASN
+ struct pasn_data *pasn;
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_IEEE80211BE
+ struct mld_info mld_info;
+ u8 mld_assoc_link_id;
+#endif /* CONFIG_IEEE80211BE */
+};
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (3)
+#define AP_DEAUTH_DELAY (1)
+/* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
+/* Number of seconds to keep STA entry after it has been deauthenticated. */
+#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx);
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_free_stas(struct hostapd_data *hapd);
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout);
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout);
+void ap_sta_no_session_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time);
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason);
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ struct vlan_description *vlan_desc);
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+ struct sta_info *sta);
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 reason);
+
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+ return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ unsigned timeout);
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta);
+int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
+
+void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* STA_INFO_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.c
new file mode 100644
index 0000000..ae157a7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.c
@@ -0,0 +1,292 @@
+/*
+ * hostapd / Client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
+ * and render them to a descriptive string. The tag number of standard options
+ * is written to the string, while the vendor ID and subtag are written for
+ * vendor options.
+ *
+ * Example strings:
+ * 0,1,50,45,221(00904c,51)
+ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "taxonomy.h"
+
+
+/* Copy a string with no funny schtuff allowed; only alphanumerics. */
+static void no_mischief_strncpy(char *dst, const char *src, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ unsigned char s = src[i];
+ int is_lower = s >= 'a' && s <= 'z';
+ int is_upper = s >= 'A' && s <= 'Z';
+ int is_digit = s >= '0' && s <= '9';
+
+ if (is_lower || is_upper || is_digit) {
+ /* TODO: if any manufacturer uses Unicode within the
+ * WPS header, it will get mangled here. */
+ dst[i] = s;
+ } else {
+ /* Note that even spaces will be transformed to
+ * underscores, so 'Nexus 7' will turn into 'Nexus_7'.
+ * This is deliberate, to make the string easier to
+ * parse. */
+ dst[i] = '_';
+ }
+ }
+}
+
+
+static int get_wps_name(char *name, size_t name_len,
+ const u8 *data, size_t data_len)
+{
+ /* Inside the WPS IE are a series of attributes, using two byte IDs
+ * and two byte lengths. We're looking for the model name, if
+ * present. */
+ while (data_len >= 4) {
+ u16 id, elen;
+
+ id = WPA_GET_BE16(data);
+ elen = WPA_GET_BE16(data + 2);
+ data += 4;
+ data_len -= 4;
+
+ if (elen > data_len)
+ return 0;
+
+ if (id == 0x1023) {
+ /* Model name, like 'Nexus 7' */
+ size_t n = (elen < name_len) ? elen : name_len;
+ no_mischief_strncpy(name, (const char *) data, n);
+ return n;
+ }
+
+ data += elen;
+ data_len -= elen;
+ }
+
+ return 0;
+}
+
+
+static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
+{
+ char *fpos = fstr;
+ char *fend = fstr + fstr_len;
+ char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
+ char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
+ char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
+ char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
+ char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
+ char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
+#define MAX_EXTCAP 254
+ char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
+ */
+ char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
+#define WPS_NAME_LEN 32
+ char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
+ * NUL */
+ int num = 0;
+ const u8 *ie;
+ size_t ie_len;
+ int ret;
+
+ os_memset(htcap, 0, sizeof(htcap));
+ os_memset(htagg, 0, sizeof(htagg));
+ os_memset(htmcs, 0, sizeof(htmcs));
+ os_memset(vhtcap, 0, sizeof(vhtcap));
+ os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
+ os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
+ os_memset(extcap, 0, sizeof(extcap));
+ os_memset(txpow, 0, sizeof(txpow));
+ os_memset(wps, 0, sizeof(wps));
+ *fpos = '\0';
+
+ if (!ies)
+ return;
+ ie = wpabuf_head(ies);
+ ie_len = wpabuf_len(ies);
+
+ while (ie_len >= 2) {
+ u8 id, elen;
+ char *sep = (num++ == 0) ? "" : ",";
+
+ id = *ie++;
+ elen = *ie++;
+ ie_len -= 2;
+
+ if (elen > ie_len)
+ break;
+
+ if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
+ /* Vendor specific */
+ if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
+ /* WPS */
+ char model_name[WPS_NAME_LEN + 1];
+ const u8 *data = &ie[4];
+ size_t data_len = elen - 4;
+
+ os_memset(model_name, 0, sizeof(model_name));
+ if (get_wps_name(model_name, WPS_NAME_LEN, data,
+ data_len)) {
+ os_snprintf(wps, sizeof(wps),
+ ",wps:%s", model_name);
+ }
+ }
+
+ ret = os_snprintf(fpos, fend - fpos,
+ "%s%d(%02x%02x%02x,%d)",
+ sep, id, ie[0], ie[1], ie[2], ie[3]);
+ } else {
+ if (id == WLAN_EID_HT_CAP && elen >= 2) {
+ /* HT Capabilities (802.11n) */
+ os_snprintf(htcap, sizeof(htcap),
+ ",htcap:%04hx",
+ WPA_GET_LE16(ie));
+ }
+ if (id == WLAN_EID_HT_CAP && elen >= 3) {
+ /* HT Capabilities (802.11n), A-MPDU information
+ */
+ os_snprintf(htagg, sizeof(htagg),
+ ",htagg:%02hx", (u16) ie[2]);
+ }
+ if (id == WLAN_EID_HT_CAP && elen >= 7) {
+ /* HT Capabilities (802.11n), MCS information */
+ os_snprintf(htmcs, sizeof(htmcs),
+ ",htmcs:%08hx",
+ (u16) WPA_GET_LE32(ie + 3));
+ }
+ if (id == WLAN_EID_VHT_CAP && elen >= 4) {
+ /* VHT Capabilities (802.11ac) */
+ os_snprintf(vhtcap, sizeof(vhtcap),
+ ",vhtcap:%08x",
+ WPA_GET_LE32(ie));
+ }
+ if (id == WLAN_EID_VHT_CAP && elen >= 8) {
+ /* VHT Capabilities (802.11ac), RX MCS
+ * information */
+ os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
+ ",vhtrxmcs:%08x",
+ WPA_GET_LE32(ie + 4));
+ }
+ if (id == WLAN_EID_VHT_CAP && elen >= 12) {
+ /* VHT Capabilities (802.11ac), TX MCS
+ * information */
+ os_snprintf(vhttxmcs, sizeof(vhttxmcs),
+ ",vhttxmcs:%08x",
+ WPA_GET_LE32(ie + 8));
+ }
+ if (id == WLAN_EID_EXT_CAPAB) {
+ /* Extended Capabilities */
+ int i;
+ int len = (elen < MAX_EXTCAP) ? elen :
+ MAX_EXTCAP;
+ char *p = extcap;
+
+ p += os_snprintf(extcap, sizeof(extcap),
+ ",extcap:");
+ for (i = 0; i < len; i++) {
+ int lim;
+
+ lim = sizeof(extcap) -
+ os_strlen(extcap);
+ if (lim <= 0)
+ break;
+ p += os_snprintf(p, lim, "%02x",
+ *(ie + i));
+ }
+ }
+ if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
+ /* TX Power */
+ os_snprintf(txpow, sizeof(txpow),
+ ",txpow:%04hx",
+ WPA_GET_LE16(ie));
+ }
+
+ ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
+ }
+ if (os_snprintf_error(fend - fpos, ret))
+ goto fail;
+ fpos += ret;
+
+ ie += elen;
+ ie_len -= elen;
+ }
+
+ ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
+ htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
+ txpow, extcap, wps);
+ if (os_snprintf_error(fend - fpos, ret)) {
+ fail:
+ fstr[0] = '\0';
+ }
+}
+
+
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+ struct sta_info *sta, char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+
+ if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
+ return 0;
+
+ ret = os_snprintf(buf, buflen, "wifi4|probe:");
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ pos = buf + ret;
+ end = buf + buflen;
+
+ ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
+ pos = os_strchr(pos, '\0');
+ if (pos >= end)
+ return 0;
+ ret = os_snprintf(pos, end - pos, "|assoc:");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
+ pos = os_strchr(pos, '\0');
+ return pos - buf;
+}
+
+
+void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ie, size_t ie_len)
+{
+ wpabuf_free(sta->probe_ie_taxonomy);
+ sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
+
+
+void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
+ struct hostapd_sta_info *info,
+ const u8 *ie, size_t ie_len)
+{
+ wpabuf_free(info->probe_ie_taxonomy);
+ info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
+
+
+void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ie, size_t ie_len)
+{
+ wpabuf_free(sta->assoc_ie_taxonomy);
+ sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.h
new file mode 100644
index 0000000..80f245c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.h
@@ -0,0 +1,24 @@
+/*
+ * hostapd / Station client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TAXONOMY_H
+#define TAXONOMY_H
+
+void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ie, size_t ie_len);
+void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
+ struct hostapd_sta_info *sta,
+ const u8 *ie, size_t ie_len);
+void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ie, size_t ie_len);
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+ struct sta_info *sta, char *buf, size_t buflen);
+
+#endif /* TAXONOMY_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.c
new file mode 100644
index 0000000..557570c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.c
@@ -0,0 +1,110 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "wpa_auth.h"
+#include "ap_drv_ops.h"
+#include "tkip_countermeasures.h"
+
+
+static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ hapd->tkip_countermeasures = 0;
+ hostapd_drv_set_countermeasures(hapd, 0);
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
+}
+
+
+static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
+
+ wpa_auth_countermeasures_start(hapd->wpa_auth);
+ hapd->tkip_countermeasures = 1;
+ hostapd_drv_set_countermeasures(hapd, 1);
+ wpa_gtk_rekey(hapd->wpa_auth);
+ eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+ eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
+ hapd, NULL);
+ while ((sta = hapd->sta_list)) {
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
+ if (sta->flags & WLAN_STA_AUTH) {
+ mlme_deauthenticate_indication(
+ hapd, sta,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ }
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ ap_free_sta(hapd, sta);
+ }
+}
+
+
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+}
+
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
+{
+ struct os_reltime now;
+ int ret = 0;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in received frame%s",
+ local ? " (local)" : "");
+
+ if (addr && local) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta != NULL) {
+ wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in "
+ "received frame");
+ mlme_michaelmicfailure_indication(hapd, addr);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLME-MICHAELMICFAILURE.indication "
+ "for not associated STA (" MACSTR
+ ") ignored", MAC2STR(addr));
+ return ret;
+ }
+ }
+
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
+ hapd->michael_mic_failures = 1;
+ } else {
+ hapd->michael_mic_failures++;
+ if (hapd->michael_mic_failures > 1) {
+ ieee80211_tkip_countermeasures_start(hapd);
+ ret = 1;
+ }
+ }
+ hapd->michael_mic_failure = now;
+
+ return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.h
new file mode 100644
index 0000000..d3eaed3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TKIP_COUNTERMEASURES_H
+#define TKIP_COUNTERMEASURES_H
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
+
+#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.c
new file mode 100644
index 0000000..0c68408
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.c
@@ -0,0 +1,2152 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/wpabuf.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "hostapd.h"
+#include "neighbor_db.h"
+#include "wps_hostapd.h"
+#include "sta_info.h"
+#include "ubus.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "rrm.h"
+#include "wnm_ap.h"
+#include "taxonomy.h"
+#include "airtime_policy.h"
+#include "hw_features.h"
+
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static int ctx_ref;
+
+static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj)
+{
+ return container_of(obj, struct hapd_interfaces, ubus);
+}
+
+static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
+{
+ return container_of(obj, struct hostapd_data, ubus.obj);
+}
+
+struct ubus_banned_client {
+ struct avl_node avl;
+ u8 addr[ETH_ALEN];
+};
+
+static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct ubus_context *ctx = eloop_ctx;
+ ubus_handle_event(ctx);
+}
+
+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
+{
+ if (ubus_reconnect(ctx, NULL)) {
+ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+ return;
+ }
+
+ eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+}
+
+static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
+{
+ eloop_unregister_read_sock(ctx->sock.fd);
+ eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+}
+
+static bool hostapd_ubus_init(void)
+{
+ if (ctx)
+ return true;
+
+ ctx = ubus_connect(NULL);
+ if (!ctx)
+ return false;
+
+ ctx->connection_lost = hostapd_ubus_connection_lost;
+ eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+ return true;
+}
+
+static void hostapd_ubus_ref_inc(void)
+{
+ ctx_ref++;
+}
+
+static void hostapd_ubus_ref_dec(void)
+{
+ ctx_ref--;
+ if (!ctx)
+ return;
+
+ if (ctx_ref)
+ return;
+
+ eloop_unregister_read_sock(ctx->sock.fd);
+ ubus_free(ctx);
+ ctx = NULL;
+}
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+ if (!hostapd_ubus_init())
+ return;
+}
+
+void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+ if (!ctx)
+ return;
+}
+
+static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
+{
+ char *event_type;
+
+ if (!ctx || !obj)
+ return;
+
+ if (asprintf(&event_type, "bss.%s", event) < 0)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_string(&b, "name", bssname);
+ ubus_notify(ctx, obj, event_type, b.head, -1);
+ free(event_type);
+}
+
+static void hostapd_send_procd_event(char *bssname, char *event)
+{
+ char *name, *s;
+ uint32_t id;
+ void *v;
+
+ if (!ctx || ubus_lookup_id(ctx, "service", &id))
+ return;
+
+ if (asprintf(&name, "hostapd.%s.%s", bssname, event) < 0)
+ return;
+
+ blob_buf_init(&b, 0);
+
+ s = blobmsg_alloc_string_buffer(&b, "type", strlen(name) + 1);
+ sprintf(s, "%s", name);
+ blobmsg_add_string_buffer(&b);
+
+ v = blobmsg_open_table(&b, "data");
+ blobmsg_close_table(&b, v);
+
+ ubus_invoke(ctx, id, "event", b.head, NULL, NULL, 1000);
+
+ free(name);
+}
+
+static void hostapd_send_shared_event(struct ubus_object *obj, char *bssname, char *event)
+{
+ hostapd_send_procd_event(bssname, event);
+ hostapd_notify_ubus(obj, bssname, event);
+}
+
+static void
+hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
+{
+ struct ubus_banned_client *ban = eloop_data;
+ struct hostapd_data *hapd = user_ctx;
+
+ avl_delete(&hapd->ubus.banned, &ban->avl);
+ free(ban);
+}
+
+static void
+hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
+{
+ struct ubus_banned_client *ban;
+
+ if (time < 0)
+ time = 0;
+
+ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+ if (!ban) {
+ if (!time)
+ return;
+
+ ban = os_zalloc(sizeof(*ban));
+ memcpy(ban->addr, addr, sizeof(ban->addr));
+ ban->avl.key = ban->addr;
+ avl_insert(&hapd->ubus.banned, &ban->avl);
+ } else {
+ eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
+ if (!time) {
+ hostapd_bss_del_ban(ban, hapd);
+ return;
+ }
+ }
+
+ eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
+}
+
+static int
+hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ int ret = hostapd_reload_config(hapd->iface, 1);
+
+ hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "reload");
+ return ret;
+}
+
+
+static void
+hostapd_parse_vht_map_blobmsg(uint16_t map)
+{
+ char label[4];
+ int16_t val;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ snprintf(label, 4, "%dss", i + 1);
+
+ val = (map & (BIT(1) | BIT(0))) + 7;
+ blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
+ map = map >> 2;
+ }
+}
+
+static void
+hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
+{
+ void *supported_mcs;
+ void *map;
+ int i;
+
+ static const struct {
+ const char *name;
+ uint32_t flag;
+ } vht_capas[] = {
+ { "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
+ { "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
+ blobmsg_add_u8(&b, vht_capas[i].name,
+ !!(vhtc->vht_capabilities_info & vht_capas[i].flag));
+
+ supported_mcs = blobmsg_open_table(&b, "mcs_map");
+
+ /* RX map */
+ map = blobmsg_open_table(&b, "rx");
+ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
+ blobmsg_close_table(&b, map);
+
+ /* TX map */
+ map = blobmsg_open_table(&b, "tx");
+ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
+ blobmsg_close_table(&b, map);
+
+ blobmsg_close_table(&b, supported_mcs);
+}
+
+static void
+hostapd_parse_capab_blobmsg(struct sta_info *sta)
+{
+ void *r, *v;
+
+ v = blobmsg_open_table(&b, "capabilities");
+
+ if (sta->vht_capabilities) {
+ r = blobmsg_open_table(&b, "vht");
+ hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
+ blobmsg_close_table(&b, r);
+ }
+
+ /* ToDo: Add HT / HE capability parsing */
+
+ blobmsg_close_table(&b, v);
+}
+
+static int
+hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct hostap_sta_driver_data sta_driver_data;
+ struct sta_info *sta;
+ void *list, *c;
+ char mac_buf[20];
+ static const struct {
+ const char *name;
+ uint32_t flag;
+ } sta_flags[] = {
+ { "auth", WLAN_STA_AUTH },
+ { "assoc", WLAN_STA_ASSOC },
+ { "authorized", WLAN_STA_AUTHORIZED },
+ { "preauth", WLAN_STA_PREAUTH },
+ { "wds", WLAN_STA_WDS },
+ { "wmm", WLAN_STA_WMM },
+ { "ht", WLAN_STA_HT },
+ { "vht", WLAN_STA_VHT },
+ { "he", WLAN_STA_HE },
+ { "wps", WLAN_STA_WPS },
+ { "mfp", WLAN_STA_MFP },
+ };
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+ list = blobmsg_open_table(&b, "clients");
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ void *r;
+ int i;
+
+ sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
+ c = blobmsg_open_table(&b, mac_buf);
+ for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
+ blobmsg_add_u8(&b, sta_flags[i].name,
+ !!(sta->flags & sta_flags[i].flag));
+
+#ifdef CONFIG_MBO
+ blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
+#endif
+
+ r = blobmsg_open_array(&b, "rrm");
+ for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
+ blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
+ blobmsg_close_array(&b, r);
+
+ r = blobmsg_open_array(&b, "extended_capabilities");
+ /* Check if client advertises extended capabilities */
+ if (sta->ext_capability && sta->ext_capability[0] > 0) {
+ for (i = 0; i < sta->ext_capability[0]; i++) {
+ blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
+ }
+ }
+ blobmsg_close_array(&b, r);
+
+ blobmsg_add_u32(&b, "aid", sta->aid);
+#ifdef CONFIG_TAXONOMY
+ r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
+ if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
+ blobmsg_add_string_buffer(&b);
+#endif
+
+ /* Driver information */
+ if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
+ r = blobmsg_open_table(&b, "bytes");
+ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
+ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
+ blobmsg_close_table(&b, r);
+ r = blobmsg_open_table(&b, "airtime");
+ blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
+ blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
+ blobmsg_close_table(&b, r);
+ r = blobmsg_open_table(&b, "packets");
+ blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
+ blobmsg_close_table(&b, r);
+ r = blobmsg_open_table(&b, "rate");
+ /* Rate in kbits */
+ blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
+ blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
+ blobmsg_close_table(&b, r);
+ blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
+ }
+
+ hostapd_parse_capab_blobmsg(sta);
+
+ blobmsg_close_table(&b, c);
+ }
+ blobmsg_close_array(&b, list);
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+static int
+hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
+ blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+/* Imported from iw/util.c
+ * https://git.kernel.org/pub/scm/linux/kernel/git/jberg/iw.git/tree/util.c?id=4b25ae3537af48dbf9d0abf94132e5ba01b32c18#n200
+ */
+int ieee80211_frequency_to_channel(int freq)
+{
+ /* see 802.11-2007 17.3.8.3.2 and Annex J */
+ if (freq == 2484)
+ return 14;
+ /* see 802.11ax D6.1 27.3.23.2 and Annex E */
+ else if (freq == 5935)
+ return 2;
+ else if (freq < 2484)
+ return (freq - 2407) / 5;
+ else if (freq >= 4910 && freq <= 4980)
+ return (freq - 4000) / 5;
+ else if (freq < 5950)
+ return (freq - 5000) / 5;
+ else if (freq <= 45000) /* DMG band lower limit */
+ /* see 802.11ax D6.1 27.3.23.2 */
+ return (freq - 5950) / 5;
+ else if (freq >= 58320 && freq <= 70200)
+ return (freq - 56160) / 2160;
+ else
+ return 0;
+}
+
+static int
+hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
+ struct os_reltime now;
+ char ssid[SSID_MAX_LEN + 1];
+ char phy_name[17];
+ size_t ssid_len = SSID_MAX_LEN;
+ u8 channel = 0, op_class = 0;
+
+ if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
+ ssid_len = hapd->conf->ssid.ssid_len;
+
+ ieee80211_freq_to_channel_ext(hapd->iface->freq,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ &op_class, &channel);
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
+ blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
+
+ memset(ssid, 0, SSID_MAX_LEN + 1);
+ memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
+ blobmsg_add_string(&b, "ssid", ssid);
+
+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+ blobmsg_add_u32(&b, "channel", channel);
+ blobmsg_add_u32(&b, "op_class", op_class);
+ blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
+#ifdef CONFIG_IEEE80211AX
+ blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
+ hapd->iface->conf->he_op.he_bss_color);
+#else
+ blobmsg_add_u32(&b, "bss_color", -1);
+#endif
+
+ snprintf(phy_name, 17, "%s", hapd->iface->phy);
+ blobmsg_add_string(&b, "phy", phy_name);
+
+ /* RRM */
+ rrm_table = blobmsg_open_table(&b, "rrm");
+ blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
+ blobmsg_close_table(&b, rrm_table);
+
+ /* WNM */
+ wnm_table = blobmsg_open_table(&b, "wnm");
+ blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
+ blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
+ blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
+ blobmsg_close_table(&b, wnm_table);
+
+ /* Airtime */
+ airtime_table = blobmsg_open_table(&b, "airtime");
+ blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
+ blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
+ blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
+ blobmsg_close_table(&b, airtime_table);
+
+ /* DFS */
+ dfs_table = blobmsg_open_table(&b, "dfs");
+ blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
+ blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
+ os_reltime_age(&hapd->iface->dfs_cac_start, &now);
+ blobmsg_add_u32(&b, "cac_seconds_left",
+ hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
+ blobmsg_close_table(&b, dfs_table);
+
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+enum {
+ NOTIFY_RESPONSE,
+ __NOTIFY_MAX
+};
+
+static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
+ [NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__NOTIFY_MAX];
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct wpabuf *elems;
+ const char *pos;
+ size_t len;
+
+ blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
+ blob_data(msg), blob_len(msg));
+
+ if (!tb[NOTIFY_RESPONSE])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
+
+ return UBUS_STATUS_OK;
+}
+
+enum {
+ DEL_CLIENT_ADDR,
+ DEL_CLIENT_REASON,
+ DEL_CLIENT_DEAUTH,
+ DEL_CLIENT_BAN_TIME,
+ __DEL_CLIENT_MAX
+};
+
+static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
+ [DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+ [DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
+ [DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
+ [DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__DEL_CLIENT_MAX];
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct sta_info *sta;
+ bool deauth = false;
+ int reason;
+ u8 addr[ETH_ALEN];
+
+ blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[DEL_CLIENT_ADDR])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (tb[DEL_CLIENT_REASON])
+ reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
+
+ if (tb[DEL_CLIENT_DEAUTH])
+ deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ if (deauth) {
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+ ap_sta_deauthenticate(hapd, sta, reason);
+ } else {
+ hostapd_drv_sta_disassoc(hapd, addr, reason);
+ ap_sta_disassociate(hapd, sta, reason);
+ }
+ }
+
+ if (tb[DEL_CLIENT_BAN_TIME])
+ hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
+
+ return 0;
+}
+
+static void
+blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
+{
+ char *s;
+
+ s = blobmsg_alloc_string_buffer(buf, name, 20);
+ sprintf(s, MACSTR, MAC2STR(addr));
+ blobmsg_add_string_buffer(buf);
+}
+
+static int
+hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct ubus_banned_client *ban;
+ void *c;
+
+ blob_buf_init(&b, 0);
+ c = blobmsg_open_array(&b, "clients");
+ avl_for_each_element(&hapd->ubus.banned, ban, avl)
+ blobmsg_add_macaddr(&b, NULL, ban->addr);
+ blobmsg_close_array(&b, c);
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+#ifdef CONFIG_WPS
+static int
+hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ int rc;
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+ rc = hostapd_wps_button_pushed(hapd, NULL);
+
+ if (rc != 0)
+ return UBUS_STATUS_NOT_SUPPORTED;
+
+ return 0;
+}
+
+
+static const char * pbc_status_enum_str(enum pbc_status status)
+{
+ switch (status) {
+ case WPS_PBC_STATUS_DISABLE:
+ return "Disabled";
+ case WPS_PBC_STATUS_ACTIVE:
+ return "Active";
+ case WPS_PBC_STATUS_TIMEOUT:
+ return "Timed-out";
+ case WPS_PBC_STATUS_OVERLAP:
+ return "Overlap";
+ default:
+ return "Unknown";
+ }
+}
+
+static int
+hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ int rc;
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+ blob_buf_init(&b, 0);
+
+ blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
+ blobmsg_add_string(&b, "last_wps_result",
+ (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
+ "Success":
+ (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
+ "Failed" : "None")));
+
+ /* If status == Failure - Add possible Reasons */
+ if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
+ hapd->wps_stats.failure_reason > 0)
+ blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
+
+ if (hapd->wps_stats.status)
+ blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
+
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+static int
+hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ int rc;
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+ rc = hostapd_wps_cancel(hapd);
+
+ if (rc != 0)
+ return UBUS_STATUS_NOT_SUPPORTED;
+
+ return 0;
+}
+#endif /* CONFIG_WPS */
+
+static int
+hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ int rc;
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+ rc = ieee802_11_set_beacon(hapd);
+
+ if (rc != 0)
+ return UBUS_STATUS_NOT_SUPPORTED;
+
+ return 0;
+}
+
+enum {
+ CONFIG_IFACE,
+ CONFIG_FILE,
+ __CONFIG_MAX
+};
+
+static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = {
+ [CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+ [CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__CONFIG_MAX];
+ struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
+ char buf[128];
+
+ blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ snprintf(buf, sizeof(buf), "bss_config=%s:%s",
+ blobmsg_get_string(tb[CONFIG_IFACE]),
+ blobmsg_get_string(tb[CONFIG_FILE]));
+
+ if (hostapd_add_iface(interfaces, buf))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_u32(&b, "pid", getpid());
+ ubus_send_reply(ctx, req, b.head);
+
+ return UBUS_STATUS_OK;
+}
+
+enum {
+ CONFIG_REM_IFACE,
+ __CONFIG_REM_MAX
+};
+
+static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = {
+ [CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__CONFIG_REM_MAX];
+ struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
+ char buf[128];
+
+ blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[CONFIG_REM_IFACE])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE])))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ return UBUS_STATUS_OK;
+}
+
+enum {
+ CSA_FREQ,
+ CSA_BCN_COUNT,
+ CSA_CENTER_FREQ1,
+ CSA_CENTER_FREQ2,
+ CSA_BANDWIDTH,
+ CSA_SEC_CHANNEL_OFFSET,
+ CSA_HT,
+ CSA_VHT,
+ CSA_HE,
+ CSA_BLOCK_TX,
+ CSA_FORCE,
+ __CSA_MAX
+};
+
+static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
+ [CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
+ [CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
+ [CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
+ [CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
+ [CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
+ [CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
+ [CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
+ [CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
+ [CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
+ [CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
+ [CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
+};
+
+
+static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_iface *iface = eloop_data;
+ struct hostapd_freq_params *freq_params = user_ctx;
+
+ hostapd_switch_channel_fallback(iface, freq_params);
+}
+
+#ifdef NEED_AP_MLME
+static int
+hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__CSA_MAX];
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct hostapd_config *iconf = hapd->iface->conf;
+ struct hostapd_freq_params *freq_params;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ struct csa_settings css = {
+ .freq_params = {
+ .ht_enabled = iconf->ieee80211n,
+ .vht_enabled = iconf->ieee80211ac,
+ .he_enabled = iconf->ieee80211ax,
+ .sec_channel_offset = iconf->secondary_channel,
+ }
+ };
+ u8 chwidth = hostapd_get_oper_chwidth(iconf);
+ u8 seg0 = 0, seg1 = 0;
+ int ret = UBUS_STATUS_OK;
+ int i;
+
+ blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[CSA_FREQ])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ switch (iconf->vht_oper_chwidth) {
+ case CHANWIDTH_USE_HT:
+ if (iconf->secondary_channel)
+ css.freq_params.bandwidth = 40;
+ else
+ css.freq_params.bandwidth = 20;
+ break;
+ case CHANWIDTH_160MHZ:
+ css.freq_params.bandwidth = 160;
+ break;
+ default:
+ css.freq_params.bandwidth = 80;
+ break;
+ }
+
+ css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
+
+#define SET_CSA_SETTING(name, field, type) \
+ do { \
+ if (tb[name]) \
+ css.field = blobmsg_get_ ## type(tb[name]); \
+ } while(0)
+
+ SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
+ SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
+ SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
+ SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
+ SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
+ SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
+ SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
+ SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
+ SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
+
+ css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
+ if (!css.freq_params.channel)
+ return UBUS_STATUS_NOT_SUPPORTED;
+
+ switch (css.freq_params.bandwidth) {
+ case 160:
+ chwidth = CHANWIDTH_160MHZ;
+ break;
+ case 80:
+ chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
+ break;
+ default:
+ chwidth = CHANWIDTH_USE_HT;
+ break;
+ }
+
+ hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
+ css.freq_params.freq,
+ css.freq_params.channel, iconf->enable_edmg,
+ iconf->edmg_channel,
+ css.freq_params.ht_enabled,
+ css.freq_params.vht_enabled,
+ css.freq_params.he_enabled,
+ css.freq_params.eht_enabled,
+ css.freq_params.sec_channel_offset,
+ chwidth, seg0, seg1,
+ iconf->vht_capab,
+ mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+ NULL,
+ mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+ NULL);
+
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+
+ if (hostapd_switch_channel(bss, &css) != 0)
+ ret = UBUS_STATUS_NOT_SUPPORTED;
+ }
+
+ if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
+ return ret;
+
+ freq_params = malloc(sizeof(*freq_params));
+ memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
+ eloop_register_timeout(0, 1, switch_chan_fallback_cb,
+ hapd->iface, freq_params);
+
+ return 0;
+#undef SET_CSA_SETTING
+}
+#endif
+
+enum {
+ VENDOR_ELEMENTS,
+ __VENDOR_ELEMENTS_MAX
+};
+
+static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
+ /* vendor elements are provided as hex-string */
+ [VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct hostapd_bss_config *bss = hapd->conf;
+ struct wpabuf *elems;
+ const char *pos;
+ size_t len;
+
+ blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
+ blob_data(msg), blob_len(msg));
+
+ if (!tb[VENDOR_ELEMENTS])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ len /= 2;
+ if (len == 0) {
+ wpabuf_free(bss->vendor_elements);
+ bss->vendor_elements = NULL;
+ return 0;
+ }
+
+ elems = wpabuf_alloc(len);
+ if (elems == NULL)
+ return 1;
+
+ if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+ wpabuf_free(elems);
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
+ wpabuf_free(bss->vendor_elements);
+ bss->vendor_elements = elems;
+
+ /* update beacons if vendor elements were set successfully */
+ if (ieee802_11_update_beacons(hapd->iface) != 0)
+ return UBUS_STATUS_NOT_SUPPORTED;
+ return UBUS_STATUS_OK;
+}
+
+static void
+hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
+{
+ const u8 *data;
+ char *str;
+ int len;
+
+ blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
+
+ str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
+ memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
+ str[nr->ssid.ssid_len] = 0;
+ blobmsg_add_string_buffer(&b);
+
+ len = wpabuf_len(nr->nr);
+ str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
+ wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
+ blobmsg_add_string_buffer(&b);
+}
+
+enum {
+ BSS_MGMT_EN_NEIGHBOR,
+ BSS_MGMT_EN_BEACON,
+ BSS_MGMT_EN_LINK_MEASUREMENT,
+#ifdef CONFIG_WNM_AP
+ BSS_MGMT_EN_BSS_TRANSITION,
+#endif
+ __BSS_MGMT_EN_MAX
+};
+
+static bool
+__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
+{
+ struct hostapd_bss_config *bss = hapd->conf;
+ uint32_t flags;
+
+ switch (flag) {
+ case BSS_MGMT_EN_NEIGHBOR:
+ if (bss->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT)
+ return false;
+
+ bss->radio_measurements[0] |=
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+ hostapd_neighbor_set_own_report(hapd);
+ return true;
+ case BSS_MGMT_EN_BEACON:
+ flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+ if (bss->radio_measurements[0] & flags == flags)
+ return false;
+
+ bss->radio_measurements[0] |= (u8) flags;
+ return true;
+ case BSS_MGMT_EN_LINK_MEASUREMENT:
+ flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+ if (bss->radio_measurements[0] & flags == flags)
+ return false;
+
+ bss->radio_measurements[0] |= (u8) flags;
+ return true;
+#ifdef CONFIG_WNM_AP
+ case BSS_MGMT_EN_BSS_TRANSITION:
+ if (bss->bss_transition)
+ return false;
+
+ bss->bss_transition = 1;
+ return true;
+#endif
+ }
+}
+
+static void
+__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
+{
+ bool update = false;
+ int i;
+
+ for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
+ if (!(flags & (1 << i)))
+ continue;
+
+ update |= __hostapd_bss_mgmt_enable_f(hapd, i);
+ }
+
+ if (update)
+ ieee802_11_update_beacons(hapd->iface);
+}
+
+
+static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
+ [BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
+ [BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
+ [BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
+#ifdef CONFIG_WNM_AP
+ [BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
+#endif
+};
+
+static int
+hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+
+{
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct blob_attr *tb[__BSS_MGMT_EN_MAX];
+ struct blob_attr *cur;
+ uint32_t flags = 0;
+ int i;
+ bool neigh = false, beacon = false;
+
+ blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
+
+ for (i = 0; i < ARRAY_SIZE(tb); i++) {
+ if (!tb[i] || !blobmsg_get_bool(tb[i]))
+ continue;
+
+ flags |= (1 << i);
+ }
+
+ __hostapd_bss_mgmt_enable(hapd, flags);
+
+ return 0;
+}
+
+
+static void
+hostapd_rrm_nr_enable(struct hostapd_data *hapd)
+{
+ __hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
+}
+
+static int
+hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct hostapd_neighbor_entry *nr;
+ void *c;
+
+ hostapd_rrm_nr_enable(hapd);
+
+ nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
+ if (!nr)
+ return UBUS_STATUS_NOT_FOUND;
+
+ blob_buf_init(&b, 0);
+
+ c = blobmsg_open_array(&b, "value");
+ hostapd_rrm_print_nr(nr);
+ blobmsg_close_array(&b, c);
+
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+static int
+hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct hostapd_neighbor_entry *nr;
+ void *c;
+
+ hostapd_rrm_nr_enable(hapd);
+ blob_buf_init(&b, 0);
+
+ c = blobmsg_open_array(&b, "list");
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+ void *cur;
+
+ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+ continue;
+
+ cur = blobmsg_open_array(&b, NULL);
+ hostapd_rrm_print_nr(nr);
+ blobmsg_close_array(&b, cur);
+ }
+ blobmsg_close_array(&b, c);
+
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
+enum {
+ NR_SET_LIST,
+ __NR_SET_LIST_MAX
+};
+
+static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
+ [NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
+};
+
+
+static void
+hostapd_rrm_nr_clear(struct hostapd_data *hapd)
+{
+ struct hostapd_neighbor_entry *nr;
+
+restart:
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+ if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+ continue;
+
+ hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
+ goto restart;
+ }
+}
+
+static int
+hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ static const struct blobmsg_policy nr_e_policy[] = {
+ { .type = BLOBMSG_TYPE_STRING },
+ { .type = BLOBMSG_TYPE_STRING },
+ { .type = BLOBMSG_TYPE_STRING },
+ };
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ struct blob_attr *tb_l[__NR_SET_LIST_MAX];
+ struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
+ struct blob_attr *cur;
+ int rem;
+
+ hostapd_rrm_nr_enable(hapd);
+
+ blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
+ if (!tb_l[NR_SET_LIST])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ hostapd_rrm_nr_clear(hapd);
+ blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
+ struct wpa_ssid_value ssid;
+ struct wpabuf *data;
+ u8 bssid[ETH_ALEN];
+ char *s, *nr_s;
+
+ blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
+ if (!tb[0] || !tb[1] || !tb[2])
+ goto invalid;
+
+ /* Neighbor Report binary */
+ nr_s = blobmsg_get_string(tb[2]);
+ data = wpabuf_parse_bin(nr_s);
+ if (!data)
+ goto invalid;
+
+ /* BSSID */
+ s = blobmsg_get_string(tb[0]);
+ if (strlen(s) == 0) {
+ /* Copy BSSID from neighbor report */
+ if (hwaddr_compact_aton(nr_s, bssid))
+ goto invalid;
+ } else if (hwaddr_aton(s, bssid)) {
+ goto invalid;
+ }
+
+ /* SSID */
+ s = blobmsg_get_string(tb[1]);
+ if (strlen(s) == 0) {
+ /* Copy SSID from hostapd BSS conf */
+ memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
+ } else {
+ ssid.ssid_len = strlen(s);
+ if (ssid.ssid_len > sizeof(ssid.ssid))
+ goto invalid;
+
+ memcpy(&ssid, s, ssid.ssid_len);
+ }
+
+ hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
+ wpabuf_free(data);
+ continue;
+
+invalid:
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
+ return 0;
+}
+
+enum {
+ BEACON_REQ_ADDR,
+ BEACON_REQ_MODE,
+ BEACON_REQ_OP_CLASS,
+ BEACON_REQ_CHANNEL,
+ BEACON_REQ_DURATION,
+ BEACON_REQ_BSSID,
+ BEACON_REQ_SSID,
+ __BEACON_REQ_MAX,
+};
+
+static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
+ [BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+ [BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
+ [BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
+ [BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
+ [BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
+ [BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
+ [BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *ureq, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct blob_attr *tb[__BEACON_REQ_MAX];
+ struct blob_attr *cur;
+ struct wpabuf *req;
+ u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ u8 addr[ETH_ALEN];
+ int mode, rem, ret;
+ int buf_len = 13;
+
+ blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
+ !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (tb[BEACON_REQ_SSID])
+ buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
+
+ mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
+ if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (tb[BEACON_REQ_BSSID] &&
+ hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ req = wpabuf_alloc(buf_len);
+ if (!req)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ /* 1: regulatory class */
+ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
+
+ /* 2: channel number */
+ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
+
+ /* 3-4: randomization interval */
+ wpabuf_put_le16(req, 0);
+
+ /* 5-6: duration */
+ wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
+
+ /* 7: mode */
+ wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
+
+ /* 8-13: BSSID */
+ wpabuf_put_data(req, bssid, ETH_ALEN);
+
+ if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
+ wpabuf_put_u8(req, WLAN_EID_SSID);
+ wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
+ wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
+ }
+
+ ret = hostapd_send_beacon_req(hapd, addr, 0, req);
+ if (ret < 0)
+ return -ret;
+
+ return 0;
+}
+
+enum {
+ LM_REQ_ADDR,
+ LM_REQ_TX_POWER_USED,
+ LM_REQ_TX_POWER_MAX,
+ __LM_REQ_MAX,
+};
+
+static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
+ [LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+ [LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
+ [LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *ureq, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct blob_attr *tb[__LM_REQ_MAX];
+ struct wpabuf *buf;
+ u8 addr[ETH_ALEN];
+ int ret;
+ int8_t txp_used, txp_max;
+
+ txp_used = 0;
+ txp_max = 0;
+
+ blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[LM_REQ_ADDR])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (tb[LM_REQ_TX_POWER_USED])
+ txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
+
+ if (tb[LM_REQ_TX_POWER_MAX])
+ txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
+
+ if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ buf = wpabuf_alloc(5);
+ if (!buf)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, 1);
+ /* TX-Power used */
+ wpabuf_put_u8(buf, txp_used);
+ /* Max TX Power */
+ wpabuf_put_u8(buf, txp_max);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+ if (ret < 0)
+ return -ret;
+
+ return 0;
+}
+
+
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
+ const u8 *pos, *end;
+ u8 token;
+
+ end = data + len;
+ token = mgmt->u.action.u.rrm.dialog_token;
+ pos = mgmt->u.action.u.rrm.variable;
+
+ if (end - pos < 8)
+ return;
+
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", mgmt->sa);
+ blobmsg_add_u16(&b, "dialog-token", token);
+ blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
+ blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
+ blobmsg_add_u16(&b, "rcpi", pos[6]);
+ blobmsg_add_u16(&b, "rsni", pos[7]);
+
+ ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
+}
+
+
+#ifdef CONFIG_WNM_AP
+
+static int
+hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
+ u16 disassoc_timer, u8 validity_period, u8 dialog_token,
+ struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
+{
+ struct blob_attr *cur;
+ struct sta_info *sta;
+ int nr_len = 0;
+ int rem;
+ u8 *nr = NULL;
+ u8 req_mode = 0;
+ u8 mbo[10];
+ size_t mbo_len = 0;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return UBUS_STATUS_NOT_FOUND;
+
+ if (neighbors) {
+ u8 *nr_cur;
+
+ if (blobmsg_check_array(neighbors,
+ BLOBMSG_TYPE_STRING) < 0)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ blobmsg_for_each_attr(cur, neighbors, rem) {
+ int len = strlen(blobmsg_get_string(cur));
+
+ if (len % 2)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ nr_len += (len / 2) + 2;
+ }
+
+ if (nr_len) {
+ nr = os_zalloc(nr_len);
+ if (!nr)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+ }
+
+ nr_cur = nr;
+ blobmsg_for_each_attr(cur, neighbors, rem) {
+ int len = strlen(blobmsg_get_string(cur)) / 2;
+
+ *nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
+ *nr_cur++ = (u8) len;
+ if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
+ free(nr);
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
+ nr_cur += len;
+ }
+ }
+
+ if (nr)
+ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+
+ if (abridged)
+ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+
+ if (disassoc_imminent)
+ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+ u8 *mbo_pos = mbo;
+
+ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = mbo_reason;
+ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = cell_pref;
+
+ if (reassoc_delay) {
+ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+ *mbo_pos++ = 2;
+ WPA_PUT_LE16(mbo_pos, reassoc_delay);
+ mbo_pos += 2;
+ }
+
+ mbo_len = mbo_pos - mbo;
+#endif
+
+ if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
+ dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ return 0;
+}
+
+enum {
+ BSS_TR_ADDR,
+ BSS_TR_DA_IMMINENT,
+ BSS_TR_DA_TIMER,
+ BSS_TR_VALID_PERIOD,
+ BSS_TR_NEIGHBORS,
+ BSS_TR_ABRIDGED,
+ BSS_TR_DIALOG_TOKEN,
+#ifdef CONFIG_MBO
+ BSS_TR_MBO_REASON,
+ BSS_TR_CELL_PREF,
+ BSS_TR_REASSOC_DELAY,
+#endif
+ __BSS_TR_DISASSOC_MAX
+};
+
+static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
+ [BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+ [BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
+ [BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
+ [BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
+ [BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
+ [BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
+ [BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
+#ifdef CONFIG_MBO
+ [BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
+ [BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
+ [BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
+#endif
+};
+
+static int
+hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *ureq, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
+ struct sta_info *sta;
+ u32 da_timer = 0;
+ u32 valid_period = 0;
+ u8 addr[ETH_ALEN];
+ u32 dialog_token = 1;
+ bool abridged;
+ bool da_imminent;
+ u8 mbo_reason;
+ u8 cell_pref;
+ u8 reassoc_delay;
+
+ blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[BSS_TR_ADDR])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (tb[BSS_TR_DA_TIMER])
+ da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
+
+ if (tb[BSS_TR_VALID_PERIOD])
+ valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
+
+ if (tb[BSS_TR_DIALOG_TOKEN])
+ dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
+
+ da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
+ abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
+
+#ifdef CONFIG_MBO
+ if (tb[BSS_TR_MBO_REASON])
+ mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
+
+ if (tb[BSS_TR_CELL_PREF])
+ cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
+
+ if (tb[BSS_TR_REASSOC_DELAY])
+ reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
+#endif
+
+ return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
+ dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
+}
+#endif
+
+#ifdef CONFIG_AIRTIME_POLICY
+enum {
+ UPDATE_AIRTIME_STA,
+ UPDATE_AIRTIME_WEIGHT,
+ __UPDATE_AIRTIME_MAX,
+};
+
+
+static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
+ [UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
+ [UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *ureq, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
+ struct sta_info *sta = NULL;
+ u8 addr[ETH_ALEN];
+ int weight;
+
+ blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[UPDATE_AIRTIME_WEIGHT])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
+
+ if (!tb[UPDATE_AIRTIME_STA]) {
+ if (!weight)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ hapd->conf->airtime_weight = weight;
+ return 0;
+ }
+
+ if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return UBUS_STATUS_NOT_FOUND;
+
+ sta->dyn_airtime_weight = weight;
+ airtime_policy_new_sta(hapd, sta);
+
+ return 0;
+}
+#endif
+
+
+static const struct ubus_method bss_methods[] = {
+ UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
+ UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
+ UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
+ UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
+#ifdef CONFIG_AIRTIME_POLICY
+ UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
+#endif
+ UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
+#ifdef CONFIG_WPS
+ UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
+ UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
+ UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
+#endif
+ UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
+ UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
+#ifdef NEED_AP_MLME
+ UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
+#endif
+ UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
+ UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
+ UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
+ UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
+ UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
+ UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
+ UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
+ UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
+#ifdef CONFIG_WNM_AP
+ UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
+#endif
+};
+
+static struct ubus_object_type bss_object_type =
+ UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
+
+static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
+{
+ return memcmp(k1, k2, ETH_ALEN);
+}
+
+void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+ struct ubus_object *obj = &hapd->ubus.obj;
+ char *name;
+ int ret;
+
+#ifdef CONFIG_MESH
+ if (hapd->conf->mesh & MESH_ENABLED)
+ return;
+#endif
+
+ if (!hostapd_ubus_init())
+ return;
+
+ if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
+ return;
+
+ avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
+ obj->name = name;
+ obj->type = &bss_object_type;
+ obj->methods = bss_object_type.methods;
+ obj->n_methods = bss_object_type.n_methods;
+ ret = ubus_add_object(ctx, obj);
+ hostapd_ubus_ref_inc();
+
+ hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "add");
+}
+
+void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+ struct ubus_object *obj = &hapd->ubus.obj;
+ char *name = (char *) obj->name;
+
+#ifdef CONFIG_MESH
+ if (hapd->conf->mesh & MESH_ENABLED)
+ return;
+#endif
+
+ if (!ctx)
+ return;
+
+ hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "remove");
+
+ if (obj->id) {
+ ubus_remove_object(ctx, obj);
+ hostapd_ubus_ref_dec();
+ }
+
+ free(name);
+}
+
+static void
+hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ const char *action)
+{
+ struct vlan_description *desc = &vlan->vlan_desc;
+ void *c;
+ int i;
+
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_string(&b, "ifname", vlan->ifname);
+ blobmsg_add_string(&b, "bridge", vlan->bridge);
+ blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
+
+ if (desc->notempty) {
+ blobmsg_add_u32(&b, "untagged", desc->untagged);
+ c = blobmsg_open_array(&b, "tagged");
+ for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
+ blobmsg_add_u32(&b, "", desc->tagged[i]);
+ blobmsg_close_array(&b, c);
+ }
+
+ ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
+}
+
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+ hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
+}
+
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+ hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
+}
+
+static const struct ubus_method daemon_methods[] = {
+ UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
+ UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),
+};
+
+static struct ubus_object_type daemon_object_type =
+ UBUS_OBJECT_TYPE("hostapd", daemon_methods);
+
+void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+ struct ubus_object *obj = &interfaces->ubus;
+ int ret;
+
+ if (!hostapd_ubus_init())
+ return;
+
+ obj->name = strdup("hostapd");
+
+ obj->type = &daemon_object_type;
+ obj->methods = daemon_object_type.methods;
+ obj->n_methods = daemon_object_type.n_methods;
+ ret = ubus_add_object(ctx, obj);
+ hostapd_ubus_ref_inc();
+}
+
+void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+ struct ubus_object *obj = &interfaces->ubus;
+ char *name = (char *) obj->name;
+
+ if (!ctx)
+ return;
+
+ if (obj->id) {
+ ubus_remove_object(ctx, obj);
+ hostapd_ubus_ref_dec();
+ }
+
+ free(name);
+}
+
+struct ubus_event_req {
+ struct ubus_notify_request nreq;
+ int resp;
+};
+
+static void
+ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
+{
+ struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
+
+ ureq->resp = ret;
+}
+
+static void hostapd_ubus_event_log(const char *fmt, ...)
+{
+ FILE *kmsg;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if ((kmsg = fopen("/dev/kmsg", "r+")) != NULL) {
+ vfprintf(kmsg, fmt, ap);
+ fclose(kmsg);
+ }
+ va_end(ap);
+}
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+ struct ubus_banned_client *ban;
+ const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
+ [HOSTAPD_UBUS_PROBE_REQ] = "probe",
+ [HOSTAPD_UBUS_AUTH_REQ] = "auth",
+ [HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
+ [HOSTAPD_UBUS_CONNECTED_REQ] = "connected",
+ [HOSTAPD_UBUS_DISCONNECTED_REQ] = "disconnected",
+ [HOSTAPD_UBUS_WPS_START] = "WPS-START",
+ [HOSTAPD_UBUS_WPS_FAIL] = "WPS-FAIL",
+ [HOSTAPD_UBUS_WPS_SUCCESS] = "WPS-SUCCESS",
+ };
+ const char *type = "mgmt";
+ struct ubus_event_req ureq = {};
+ const u8 *addr;
+
+ /* ignore first three events */
+ if (req->type <= HOSTAPD_UBUS_ASSOC_REQ)
+ return 0;
+
+ hostapd_ubus_event_log("STA " MACSTR " %s\n", MAC2STR(req->addr),
+ types[req->type]);
+
+ if (req->mgmt_frame)
+ addr = req->mgmt_frame->sa;
+ else
+ addr = req->addr;
+
+ ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+ if (ban)
+ return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+ if (!hapd->ubus.obj.has_subscribers)
+ return WLAN_STATUS_SUCCESS;
+
+ if (req->type < ARRAY_SIZE(types))
+ type = types[req->type];
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", addr);
+ if (req->mgmt_frame)
+ blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
+ if (req->ssi_signal)
+ blobmsg_add_u32(&b, "signal", req->ssi_signal);
+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+
+ if (req->elems) {
+ if(req->elems->ht_capabilities)
+ {
+ struct ieee80211_ht_capabilities *ht_capabilities;
+ void *ht_cap, *ht_cap_mcs_set, *mcs_set;
+
+
+ ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
+ ht_cap = blobmsg_open_table(&b, "ht_capabilities");
+ blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
+ ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
+ blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
+ blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
+ blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
+ blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
+ mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
+ for (int i = 0; i < 16; i++) {
+ blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
+ }
+ blobmsg_close_array(&b, mcs_set);
+ blobmsg_close_table(&b, ht_cap_mcs_set);
+ blobmsg_close_table(&b, ht_cap);
+ }
+ if(req->elems->vht_capabilities)
+ {
+ struct ieee80211_vht_capabilities *vht_capabilities;
+ void *vht_cap, *vht_cap_mcs_set;
+
+ vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
+ vht_cap = blobmsg_open_table(&b, "vht_capabilities");
+ blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
+ vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
+ blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
+ blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
+ blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
+ blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
+ blobmsg_close_table(&b, vht_cap_mcs_set);
+ blobmsg_close_table(&b, vht_cap);
+ }
+ }
+
+ if (!hapd->ubus.notify_response) {
+ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
+ return WLAN_STATUS_SUCCESS;
+
+ ureq.nreq.status_cb = ubus_event_cb;
+ ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+ if (ureq.resp)
+ return ureq.resp;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
+{
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ if (!addr)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", addr);
+
+ ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+}
+
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ const char *auth_alg)
+{
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", sta->addr);
+ if (auth_alg)
+ blobmsg_add_string(&b, "auth-alg", auth_alg);
+
+ ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
+}
+
+void hostapd_ubus_notify_beacon_report(
+ struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
+ struct rrm_measurement_beacon_report *rep, size_t len)
+{
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ if (!addr || !rep)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", addr);
+ blobmsg_add_u16(&b, "op-class", rep->op_class);
+ blobmsg_add_u16(&b, "channel", rep->channel);
+ blobmsg_add_u64(&b, "start-time", rep->start_time);
+ blobmsg_add_u16(&b, "duration", rep->duration);
+ blobmsg_add_u16(&b, "report-info", rep->report_info);
+ blobmsg_add_u16(&b, "rcpi", rep->rcpi);
+ blobmsg_add_u16(&b, "rsni", rep->rsni);
+ blobmsg_add_macaddr(&b, "bssid", rep->bssid);
+ blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
+ blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
+ blobmsg_add_u16(&b, "rep-mode", rep_mode);
+
+ ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
+}
+
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+ int chan_width, int cf1, int cf2)
+{
+ struct hostapd_data *hapd;
+ int i;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_u16(&b, "frequency", frequency);
+ blobmsg_add_u16(&b, "width", chan_width);
+ blobmsg_add_u16(&b, "center1", cf1);
+ blobmsg_add_u16(&b, "center2", cf2);
+
+ for (i = 0; i < iface->num_bss; i++) {
+ hapd = iface->bss[i];
+ ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
+ }
+}
+
+#ifdef CONFIG_WNM_AP
+static void hostapd_ubus_notify_bss_transition_add_candidate_list(
+ const u8 *candidate_list, u16 candidate_list_len)
+{
+ char *cl_str;
+ int i;
+
+ if (candidate_list_len == 0)
+ return;
+
+ cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
+ for (i = 0; i < candidate_list_len; i++)
+ snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
+ blobmsg_add_string_buffer(&b);
+
+}
+#endif
+
+void hostapd_ubus_notify_bss_transition_response(
+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+ u8 bss_termination_delay, const u8 *target_bssid,
+ const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+ u16 i;
+
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ if (!addr)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", addr);
+ blobmsg_add_u8(&b, "dialog-token", dialog_token);
+ blobmsg_add_u8(&b, "status-code", status_code);
+ blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
+ if (target_bssid)
+ blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
+
+ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
+#endif
+}
+
+int hostapd_ubus_notify_bss_transition_query(
+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+ const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+ struct ubus_event_req ureq = {};
+ char *cl_str;
+ u16 i;
+
+ if (!hapd->ubus.obj.has_subscribers)
+ return 0;
+
+ if (!addr)
+ return 0;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_macaddr(&b, "address", addr);
+ blobmsg_add_u8(&b, "dialog-token", dialog_token);
+ blobmsg_add_u8(&b, "reason", reason);
+ hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+ if (!hapd->ubus.notify_response) {
+ ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
+ return 0;
+ }
+
+ if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
+ return 0;
+
+ ureq.nreq.status_cb = ubus_event_cb;
+ ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+ return ureq.resp;
+#endif
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.h
new file mode 100644
index 0000000..1a493e1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.h
@@ -0,0 +1,159 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef __HOSTAPD_UBUS_H
+#define __HOSTAPD_UBUS_H
+
+enum hostapd_ubus_event_type {
+ HOSTAPD_UBUS_PROBE_REQ,
+ HOSTAPD_UBUS_AUTH_REQ,
+ HOSTAPD_UBUS_ASSOC_REQ,
+ HOSTAPD_UBUS_CONNECTED_REQ,
+ HOSTAPD_UBUS_DISCONNECTED_REQ,
+ HOSTAPD_UBUS_WPS_START,
+ HOSTAPD_UBUS_WPS_FAIL,
+ HOSTAPD_UBUS_WPS_SUCCESS,
+ HOSTAPD_UBUS_TYPE_MAX
+};
+
+struct hostapd_ubus_request {
+ enum hostapd_ubus_event_type type;
+ const struct ieee80211_mgmt *mgmt_frame;
+ const struct ieee802_11_elems *elems;
+ int ssi_signal; /* dBm */
+ const u8 *addr;
+};
+
+struct hostapd_iface;
+struct hostapd_data;
+struct hapd_interfaces;
+struct rrm_measurement_beacon_report;
+
+#ifdef UBUS_SUPPORT
+
+#include <libubox/avl.h>
+#include <libubus.h>
+
+struct hostapd_ubus_bss {
+ struct ubus_object obj;
+ struct avl_tree banned;
+ int notify_response;
+};
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface);
+void hostapd_ubus_free_iface(struct hostapd_iface *iface);
+void hostapd_ubus_add_bss(struct hostapd_data *hapd);
+void hostapd_ubus_free_bss(struct hostapd_data *hapd);
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
+void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+ const u8 *addr, u8 token, u8 rep_mode,
+ struct rrm_measurement_beacon_report *rep,
+ size_t len);
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+ int chan_width, int cf1, int cf2);
+
+void hostapd_ubus_notify_bss_transition_response(
+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+ u8 bss_termination_delay, const u8 *target_bssid,
+ const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_add(struct hapd_interfaces *interfaces);
+void hostapd_ubus_free(struct hapd_interfaces *interfaces);
+int hostapd_ubus_notify_bss_transition_query(
+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+ const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ const char *auth_alg);
+
+#else
+
+struct hostapd_ubus_bss {};
+
+static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+ return 0;
+}
+
+static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+}
+
+static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
+{
+}
+
+static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+ const u8 *addr, u8 token,
+ u8 rep_mode,
+ struct rrm_measurement_beacon_report *rep,
+ size_t len)
+{
+}
+static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+ int chan_width, int cf1, int cf2)
+{
+}
+
+static inline void hostapd_ubus_notify_bss_transition_response(
+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+ u8 bss_termination_delay, const u8 *target_bssid,
+ const u8 *candidate_list, u16 candidate_list_len)
+{
+}
+
+static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline int hostapd_ubus_notify_bss_transition_query(
+ struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+ const u8 *candidate_list, u16 candidate_list_len)
+{
+ return 0;
+}
+
+static inline void
+hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ const char *auth_alg)
+{
+}
+
+#endif
+
+#endif
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/utils.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/utils.c
new file mode 100644
index 0000000..e93e531
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/utils.c
@@ -0,0 +1,112 @@
+/*
+ * AP mode helper functions
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "fst/fst.h"
+#include "sta_info.h"
+#include "hostapd.h"
+
+
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+ int (*cb)(void *ctx, const u8 *sa,
+ const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal),
+ void *ctx)
+{
+ struct hostapd_probereq_cb *n;
+
+ n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
+ sizeof(struct hostapd_probereq_cb));
+ if (n == NULL)
+ return -1;
+
+ hapd->probereq_cb = n;
+ n = &hapd->probereq_cb[hapd->num_probereq_cb];
+ hapd->num_probereq_cb++;
+
+ n->cb = cb;
+ n->ctx = ctx;
+
+ return 0;
+}
+
+
+struct prune_data {
+ struct hostapd_data *hapd;
+ const u8 *addr;
+ int mld_assoc_link_id;
+};
+
+static int prune_associations(struct hostapd_iface *iface, void *ctx)
+{
+ struct prune_data *data = ctx;
+ struct sta_info *osta;
+ struct hostapd_data *ohapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ ohapd = iface->bss[j];
+ if (ohapd == data->hapd)
+ continue;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (ohapd->conf->skip_prune_assoc)
+ continue;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_FST
+ /* Don't prune STAs belong to same FST */
+ if (ohapd->iface->fst &&
+ data->hapd->iface->fst &&
+ fst_are_ifaces_aggregated(ohapd->iface->fst,
+ data->hapd->iface->fst))
+ continue;
+#endif /* CONFIG_FST */
+ osta = ap_get_sta(ohapd, data->addr);
+ if (!osta)
+ continue;
+
+#ifdef CONFIG_IEEE80211BE
+ if (data->mld_assoc_link_id >= 0 &&
+ osta->mld_assoc_link_id == data->mld_assoc_link_id)
+ continue;
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
+ ohapd->conf->iface, MAC2STR(osta->addr));
+ ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
+ }
+
+ return 0;
+}
+
+/**
+ * hostapd_prune_associations - Remove extraneous associations
+ * @hapd: Pointer to BSS data for the most recent association
+ * @addr: Associated STA address
+ * @mld_assoc_link_id: MLD link id used for association or -1 for non MLO
+ *
+ * This function looks through all radios and BSS's for previous
+ * (stale) associations of STA. If any are found they are removed.
+ */
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
+ int mld_assoc_link_id)
+{
+ struct prune_data data;
+
+ data.hapd = hapd;
+ data.addr = addr;
+ data.mld_assoc_link_id = mld_assoc_link_id;
+
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface)
+ hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, prune_associations, &data);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.c
new file mode 100644
index 0000000..b6f6bb1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.c
@@ -0,0 +1,34 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "ap/vlan.h"
+
+/* compare the two arguments, NULL is treated as empty
+ * return zero iff they are equal
+ */
+int vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+ int i;
+ const int a_empty = !a || !a->notempty;
+ const int b_empty = !b || !b->notempty;
+
+ if (a_empty && b_empty)
+ return 0;
+ if (a_empty || b_empty)
+ return 1;
+ if (a->untagged != b->untagged)
+ return 1;
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
+ if (a->tagged[i] != b->tagged[i])
+ return 1;
+ }
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.h
new file mode 100644
index 0000000..af84929
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.h
@@ -0,0 +1,30 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_H
+#define VLAN_H
+
+#define MAX_NUM_TAGGED_VLAN 32
+
+struct vlan_description {
+ int notempty; /* 0 : no vlan information present, 1: else */
+ int untagged; /* >0 802.1q vid */
+ int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
+};
+
+#ifndef CONFIG_NO_VLAN
+int vlan_compare(struct vlan_description *a, struct vlan_description *b);
+#else /* CONFIG_NO_VLAN */
+static inline int
+vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+ return 0;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_full.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_full.c
new file mode 100644
index 0000000..053d633
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_full.c
@@ -0,0 +1,805 @@
+/*
+ * hostapd / VLAN initialization - full dynamic VLAN
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
+#undef if_type
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "drivers/priv_netlink.h"
+#include "drivers/linux_ioctl.h"
+#include "common/linux_bridge.h"
+#include "common/linux_vlan.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "vlan_init.h"
+#include "vlan_util.h"
+
+
+struct full_dynamic_vlan {
+ int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+#define DVLAN_CLEAN_BR 0x1
+#define DVLAN_CLEAN_VLAN 0x2
+#define DVLAN_CLEAN_VLAN_PORT 0x4
+
+struct dynamic_iface {
+ char ifname[IFNAMSIZ + 1];
+ int usage;
+ int clean;
+ struct dynamic_iface *next;
+};
+
+
+/* Increment ref counter for ifname and add clean flag.
+ * If not in list, add it only if some flags are given.
+ */
+static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
+ int clean)
+{
+ struct dynamic_iface *next, **dynamic_ifaces;
+ struct hapd_interfaces *interfaces;
+
+ interfaces = hapd->iface->interfaces;
+ dynamic_ifaces = &interfaces->vlan_priv;
+
+ for (next = *dynamic_ifaces; next; next = next->next) {
+ if (os_strcmp(ifname, next->ifname) == 0)
+ break;
+ }
+
+ if (next) {
+ next->usage++;
+ next->clean |= clean;
+ return;
+ }
+
+ if (!clean)
+ return;
+
+ next = os_zalloc(sizeof(*next));
+ if (!next)
+ return;
+ os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
+ next->usage = 1;
+ next->clean = clean;
+ next->next = *dynamic_ifaces;
+ *dynamic_ifaces = next;
+}
+
+
+/* Decrement reference counter for given ifname.
+ * Return clean flag iff reference counter was decreased to zero, else zero
+ */
+static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
+{
+ struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
+ struct hapd_interfaces *interfaces;
+ int clean;
+
+ interfaces = hapd->iface->interfaces;
+ dynamic_ifaces = &interfaces->vlan_priv;
+
+ for (next = *dynamic_ifaces; next; next = next->next) {
+ if (os_strcmp(ifname, next->ifname) == 0)
+ break;
+ prev = next;
+ }
+
+ if (!next)
+ return 0;
+
+ next->usage--;
+ if (next->usage)
+ return 0;
+
+ if (prev)
+ prev->next = next->next;
+ else
+ *dynamic_ifaces = next->next;
+ clean = next->clean;
+ os_free(next);
+
+ return clean;
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+ return ifconfig_helper(if_name, 0);
+}
+
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS 256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+ int fd;
+ struct ifreq ifr;
+ unsigned long args[2];
+ int if_index;
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ if (linux_br_del_if(fd, br_name, if_name) == 0)
+ goto done;
+
+ if_index = if_nametoindex(if_name);
+
+ if (if_index == 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+ "interface index for '%s'",
+ __func__, if_name);
+ close(fd);
+ return -1;
+ }
+
+ args[0] = BRCTL_DEL_IF;
+ args[1] = if_index;
+
+ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *) args;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+ /* No error if interface already removed. */
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+ "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+ "%s", __func__, br_name, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+done:
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add interface 'if_name' to the bridge 'br_name'
+
+ returns -1 on error
+ returns 1 if the interface is already part of the bridge
+ returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+ int fd;
+ struct ifreq ifr;
+ unsigned long args[2];
+ int if_index;
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ if (linux_br_add_if(fd, br_name, if_name) == 0)
+ goto done;
+ if (errno == EBUSY) {
+ /* The interface is already added. */
+ close(fd);
+ return 1;
+ }
+
+ if_index = if_nametoindex(if_name);
+
+ if (if_index == 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+ "interface index for '%s'",
+ __func__, if_name);
+ close(fd);
+ return -1;
+ }
+
+ args[0] = BRCTL_ADD_IF;
+ args[1] = if_index;
+
+ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *) args;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ if (errno == EBUSY) {
+ /* The interface is already added. */
+ close(fd);
+ return 1;
+ }
+
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+ "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+ "%s", __func__, br_name, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+done:
+ close(fd);
+ return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+ int fd;
+ unsigned long arg[2];
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ if (linux_br_del(fd, br_name) == 0)
+ goto done;
+
+ arg[0] = BRCTL_DEL_BRIDGE;
+ arg[1] = (unsigned long) br_name;
+
+ if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+ /* No error if bridge already removed. */
+ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+ "%s: %s", __func__, br_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+done:
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add a bridge with the name 'br_name'.
+
+ returns -1 on error
+ returns 1 if the bridge already exists
+ returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+ int fd;
+ unsigned long arg[4];
+ struct ifreq ifr;
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ if (linux_br_add(fd, br_name) == 0)
+ goto done;
+ if (errno == EEXIST) {
+ /* The bridge is already added. */
+ close(fd);
+ return 1;
+ }
+
+ arg[0] = BRCTL_ADD_BRIDGE;
+ arg[1] = (unsigned long) br_name;
+
+ if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+ if (errno == EEXIST) {
+ /* The bridge is already added. */
+ close(fd);
+ return 1;
+ } else {
+ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+ "failed for %s: %s",
+ __func__, br_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+done:
+ /* Decrease forwarding delay to avoid EAPOL timeouts. */
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+ arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+ arg[1] = 1;
+ arg[2] = 0;
+ arg[3] = 0;
+ ifr.ifr_data = (char *) &arg;
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: "
+ "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+ "%s: %s", __func__, br_name, strerror(errno));
+ /* Continue anyway */
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+ int fd;
+ int i;
+ int port_cnt = 0;
+ unsigned long arg[4];
+ int ifindices[MAX_BR_PORTS];
+ struct ifreq ifr;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ arg[0] = BRCTL_GET_PORT_LIST;
+ arg[1] = (unsigned long) ifindices;
+ arg[2] = MAX_BR_PORTS;
+ arg[3] = 0;
+
+ os_memset(ifindices, 0, sizeof(ifindices));
+ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *) arg;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+ "failed for %s: %s",
+ __func__, br_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ for (i = 1; i < MAX_BR_PORTS; i++) {
+ if (ifindices[i] > 0) {
+ port_cnt++;
+ }
+ }
+
+ close(fd);
+ return port_cnt;
+}
+
+
+static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
+ const char *br_name, int vid,
+ struct hostapd_data *hapd)
+{
+ char vlan_ifname[IFNAMSIZ];
+ int clean;
+ int ret;
+
+ if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+ tagged_interface, vid);
+ else
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+ vid);
+ if (ret >= (int) sizeof(vlan_ifname))
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan_ifname);
+
+ clean = 0;
+ ifconfig_up(tagged_interface);
+ if (!vlan_add(tagged_interface, vid, vlan_ifname))
+ clean |= DVLAN_CLEAN_VLAN;
+
+ if (!br_addif(br_name, vlan_ifname))
+ clean |= DVLAN_CLEAN_VLAN_PORT;
+
+ dyn_iface_get(hapd, vlan_ifname, clean);
+
+ ifconfig_up(vlan_ifname);
+}
+
+
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan, int vid)
+{
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int ret;
+
+ if (vlan->bridge[0]) {
+ os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
+ ret = 0;
+ } else if (hapd->conf->vlan_bridge[0]) {
+ ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
+ hapd->conf->vlan_bridge, vid);
+ } else if (tagged_interface) {
+ ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
+ tagged_interface, vid);
+ } else {
+ ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
+ }
+ if (ret >= IFNAMSIZ)
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ br_name);
+}
+
+
+static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
+ int vid)
+{
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+ dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+
+ ifconfig_up(br_name);
+
+ if (tagged_interface)
+ vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
+ vid, hapd);
+}
+
+
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+{
+ char br_name[IFNAMSIZ];
+ struct hostapd_vlan *vlan;
+ int untagged, *tagged, i, notempty;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+ for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+ if (vlan->configured ||
+ os_strcmp(ifname, vlan->ifname) != 0)
+ continue;
+ break;
+ }
+ if (!vlan)
+ return;
+
+ if (hapd->conf->ssid.vlan_no_bridge)
+ goto out;
+
+ vlan->configured = 1;
+
+ notempty = vlan->vlan_desc.notempty;
+ untagged = vlan->vlan_desc.untagged;
+ tagged = vlan->vlan_desc.tagged;
+
+ if (!notempty) {
+ /* Non-VLAN STA */
+ if (hapd->conf->bridge[0] &&
+ !br_addif(hapd->conf->bridge, ifname))
+ vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+ } else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+ vlan_bridge_name(br_name, hapd, vlan, untagged);
+
+ vlan_get_bridge(br_name, hapd, untagged);
+
+ if (!br_addif(br_name, ifname))
+ vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+ }
+
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+ if (tagged[i] == untagged ||
+ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+ (i > 0 && tagged[i] == tagged[i - 1]))
+ continue;
+ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
+ vlan_get_bridge(br_name, hapd, tagged[i]);
+ vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+ ifname, br_name, tagged[i], hapd);
+ }
+
+out:
+ ifconfig_up(ifname);
+}
+
+
+static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
+ const char *br_name, int vid,
+ struct hostapd_data *hapd)
+{
+ char vlan_ifname[IFNAMSIZ];
+ int clean;
+ int ret;
+
+ if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+ tagged_interface, vid);
+ else
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+ vid);
+ if (ret >= (int) sizeof(vlan_ifname))
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan_ifname);
+
+
+ clean = dyn_iface_put(hapd, vlan_ifname);
+
+ if (clean & DVLAN_CLEAN_VLAN_PORT)
+ br_delif(br_name, vlan_ifname);
+
+ if (clean & DVLAN_CLEAN_VLAN) {
+ ifconfig_down(vlan_ifname);
+ vlan_rem(vlan_ifname);
+ }
+}
+
+
+static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
+ int vid)
+{
+ int clean;
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+ if (tagged_interface)
+ vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
+ vid, hapd);
+
+ clean = dyn_iface_put(hapd, br_name);
+ if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
+ ifconfig_down(br_name);
+ br_delbr(br_name);
+ }
+}
+
+
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
+{
+ struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+ first = prev = vlan;
+
+ while (vlan) {
+ if (os_strcmp(ifname, vlan->ifname) != 0) {
+ prev = vlan;
+ vlan = vlan->next;
+ continue;
+ }
+ break;
+ }
+ if (!vlan)
+ return;
+
+ if (vlan->configured) {
+ int notempty = vlan->vlan_desc.notempty;
+ int untagged = vlan->vlan_desc.untagged;
+ int *tagged = vlan->vlan_desc.tagged;
+ char br_name[IFNAMSIZ];
+ int i;
+
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+ if (tagged[i] == untagged ||
+ tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+ (i > 0 && tagged[i] == tagged[i - 1]))
+ continue;
+ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
+ vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+ ifname, br_name, tagged[i], hapd);
+ vlan_put_bridge(br_name, hapd, tagged[i]);
+ }
+
+ if (!notempty) {
+ /* Non-VLAN STA */
+ if (hapd->conf->bridge[0] &&
+ (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
+ br_delif(hapd->conf->bridge, ifname);
+ } else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+ vlan_bridge_name(br_name, hapd, vlan, untagged);
+
+ if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+ br_delif(br_name, vlan->ifname);
+
+ vlan_put_bridge(br_name, hapd, untagged);
+ }
+ }
+
+ /*
+ * Ensure this VLAN interface is actually removed even if
+ * NEWLINK message is only received later.
+ */
+ if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
+ wpa_printf(MSG_ERROR,
+ "VLAN: Could not remove VLAN iface: %s: %s",
+ vlan->ifname, strerror(errno));
+
+ if (vlan == first)
+ hapd->conf->vlan = vlan->next;
+ else
+ prev->next = vlan->next;
+
+ os_free(vlan);
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+ struct hostapd_data *hapd)
+{
+ struct ifinfomsg *ifi;
+ int attrlen, nlmsg_len, rta_len;
+ struct rtattr *attr;
+ char ifname[IFNAMSIZ + 1];
+
+ if (len < sizeof(*ifi))
+ return;
+
+ ifi = NLMSG_DATA(h);
+
+ nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+ attrlen = h->nlmsg_len - nlmsg_len;
+ if (attrlen < 0)
+ return;
+
+ attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+ os_memset(ifname, 0, sizeof(ifname));
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ if (attr->rta_type == IFLA_IFNAME) {
+ int n = attr->rta_len - rta_len;
+ if (n < 0)
+ break;
+
+ if ((size_t) n >= sizeof(ifname))
+ n = sizeof(ifname) - 1;
+ os_memcpy(ifname, ((char *) attr) + rta_len, n);
+
+ }
+
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+ if (!ifname[0])
+ return;
+ if (del && if_nametoindex(ifname)) {
+ /* interface still exists, race condition ->
+ * iface has just been recreated */
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+ del ? "DEL" : "NEW",
+ ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+ (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+ (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+ (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+ (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ if (del)
+ vlan_dellink(ifname, hapd);
+ else
+ vlan_newlink(ifname, hapd);
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ char buf[8192];
+ int left;
+ struct sockaddr_nl from;
+ socklen_t fromlen;
+ struct nlmsghdr *h;
+ struct hostapd_data *hapd = eloop_ctx;
+
+ fromlen = sizeof(from);
+ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *) &from, &fromlen);
+ if (left < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+ __func__, strerror(errno));
+ return;
+ }
+
+ h = (struct nlmsghdr *) buf;
+ while (NLMSG_OK(h, left)) {
+ int len, plen;
+
+ len = h->nlmsg_len;
+ plen = len - sizeof(*h);
+ if (len > left || plen < 0) {
+ wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+ "message: len=%d left=%d plen=%d",
+ len, left, plen);
+ break;
+ }
+
+ switch (h->nlmsg_type) {
+ case RTM_NEWLINK:
+ vlan_read_ifnames(h, plen, 0, hapd);
+ break;
+ case RTM_DELLINK:
+ vlan_read_ifnames(h, plen, 1, hapd);
+ break;
+ }
+
+ h = NLMSG_NEXT(h, left);
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+ "netlink message", __func__, left);
+ }
+}
+
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+ struct sockaddr_nl local;
+ struct full_dynamic_vlan *priv;
+
+ priv = os_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ return NULL;
+
+ vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+ DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+
+ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (priv->s < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+ "NETLINK_ROUTE) failed: %s",
+ __func__, strerror(errno));
+ os_free(priv);
+ return NULL;
+ }
+
+ os_memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = RTMGRP_LINK;
+ if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+ __func__, strerror(errno));
+ close(priv->s);
+ os_free(priv);
+ return NULL;
+ }
+
+ if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+ {
+ close(priv->s);
+ os_free(priv);
+ return NULL;
+ }
+
+ return priv;
+}
+
+
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+ if (priv == NULL)
+ return;
+ eloop_unregister_read_sock(priv->s);
+ close(priv->s);
+ os_free(priv);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ifconfig.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ifconfig.c
new file mode 100644
index 0000000..ef953a5
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ifconfig.c
@@ -0,0 +1,69 @@
+/*
+ * hostapd / VLAN ifconfig helpers
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "vlan_util.h"
+
+
+int ifconfig_helper(const char *if_name, int up)
+{
+ int fd;
+ struct ifreq ifr;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+ "for interface %s: %s",
+ __func__, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (up)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+ "for interface %s (up=%d): %s",
+ __func__, if_name, up, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+int ifconfig_up(const char *if_name)
+{
+ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+ return ifconfig_helper(if_name, 1);
+}
+
+
+int iface_exists(const char *ifname)
+{
+ return if_nametoindex(ifname);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.c
new file mode 100644
index 0000000..b69f3de
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.c
@@ -0,0 +1,273 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "vlan_init.h"
+#include "vlan_util.h"
+
+
+static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ int existsok)
+{
+ bool vlan_exists = iface_exists(vlan->ifname);
+ int ret;
+#ifdef CONFIG_WEP
+ int i;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (!hapd->conf->ssid.wep.key[i])
+ continue;
+ wpa_printf(MSG_ERROR,
+ "VLAN: Refusing to set up VLAN iface %s with WEP",
+ vlan->ifname);
+ return -1;
+ }
+#endif /* CONFIG_WEP */
+
+ if (!vlan_exists)
+ ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+ else if (!existsok)
+ return -1;
+ else
+ ret = 0;
+
+ if (ret)
+ return ret;
+
+ ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
+ if (hapd->wpa_auth)
+ ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+ if (!ret && !vlan_exists)
+ hostapd_ubus_add_vlan(hapd, vlan);
+
+ if (ret == 0)
+ return ret;
+
+ wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
+ vlan->vlan_id, ret);
+ if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
+ wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+ /* group state machine setup failed */
+ if (hostapd_vlan_if_remove(hapd, vlan->ifname))
+ wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+ return ret;
+}
+
+
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+ int ret;
+
+ ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "WPA deinitialization for VLAN %d failed (%d)",
+ vlan->vlan_id, ret);
+
+ hostapd_ubus_remove_vlan(hapd, vlan);
+
+ return hostapd_vlan_if_remove(hapd, vlan->ifname);
+}
+
+
+static int vlan_dynamic_add(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan)
+{
+ while (vlan) {
+ if (vlan->vlan_id != VLAN_ID_WILDCARD) {
+ if (vlan_if_add(hapd, vlan, 1)) {
+ wpa_printf(MSG_ERROR,
+ "VLAN: Could not add VLAN %s: %s",
+ vlan->ifname, strerror(errno));
+ return -1;
+ }
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ vlan_newlink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+ }
+
+ vlan = vlan->next;
+ }
+
+ return 0;
+}
+
+
+static void vlan_dynamic_remove(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan)
+{
+ struct hostapd_vlan *next;
+
+ while (vlan) {
+ next = vlan->next;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ /* vlan_dellink() takes care of cleanup and interface removal */
+ if (vlan->vlan_id != VLAN_ID_WILDCARD)
+ vlan_dellink(vlan->ifname, hapd);
+#else /* CONFIG_FULL_DYNAMIC_VLAN */
+ if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+ vlan_if_remove(hapd, vlan)) {
+ wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
+ "iface: %s: %s",
+ vlan->ifname, strerror(errno));
+ }
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ vlan = next;
+ }
+}
+
+
+int vlan_init(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
+ hapd->conf->ssid.per_sta_vif) &&
+ !hapd->conf->vlan) {
+ /* dynamic vlans enabled but no (or empty) vlan_file given */
+ struct hostapd_vlan *vlan;
+ int ret;
+
+ vlan = os_zalloc(sizeof(*vlan));
+ if (vlan == NULL) {
+ wpa_printf(MSG_ERROR, "Out of memory while assigning "
+ "VLAN interfaces");
+ return -1;
+ }
+
+ vlan->vlan_id = VLAN_ID_WILDCARD;
+ ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+ hapd->conf->iface);
+ if (ret >= (int) sizeof(vlan->ifname)) {
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan->ifname);
+ } else if (ret < 0) {
+ os_free(vlan);
+ return ret;
+ }
+ vlan->next = hapd->conf->vlan;
+ hapd->conf->vlan = vlan;
+ }
+
+ if (vlan_dynamic_add(hapd, hapd->conf->vlan))
+ return -1;
+
+ return 0;
+}
+
+
+void vlan_deinit(struct hostapd_data *hapd)
+{
+ vlan_dynamic_remove(hapd, hapd->conf->vlan);
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+ hapd->full_dynamic_vlan = NULL;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+}
+
+
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+ int vlan_id,
+ struct vlan_description *vlan_desc)
+{
+ struct hostapd_vlan *n;
+ char ifname[IFNAMSIZ + 1], *pos;
+ int ret;
+
+ if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+ __func__, vlan_id, vlan->ifname);
+ os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
+ pos = os_strchr(ifname, '#');
+ if (pos == NULL)
+ return NULL;
+ *pos++ = '\0';
+
+ n = os_zalloc(sizeof(*n));
+ if (n == NULL)
+ return NULL;
+
+ n->vlan_id = vlan_id;
+ if (vlan_desc)
+ n->vlan_desc = *vlan_desc;
+ n->dynamic_vlan = 1;
+
+ ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
+ ifname, vlan_id, pos);
+ if (os_snprintf_error(sizeof(n->ifname), ret)) {
+ os_free(n);
+ return NULL;
+ }
+ os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
+
+ n->next = hapd->conf->vlan;
+ hapd->conf->vlan = n;
+
+ /* hapd->conf->vlan needs this new VLAN here for WPA setup */
+ if (vlan_if_add(hapd, n, 0)) {
+ hapd->conf->vlan = n->next;
+ os_free(n);
+ n = NULL;
+ }
+
+ return n;
+}
+
+
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+ struct hostapd_vlan *vlan;
+
+ if (vlan_id <= 0)
+ return 1;
+
+ wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
+ __func__, hapd->conf->iface, vlan_id);
+
+ vlan = hapd->conf->vlan;
+ while (vlan) {
+ if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
+ vlan->dynamic_vlan--;
+ break;
+ }
+ vlan = vlan->next;
+ }
+
+ if (vlan == NULL)
+ return 1;
+
+ if (vlan->dynamic_vlan == 0) {
+ vlan_if_remove(hapd, vlan);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+ }
+
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.h
new file mode 100644
index 0000000..d17c82c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.h
@@ -0,0 +1,44 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_INIT_H
+#define VLAN_INIT_H
+
+#ifndef CONFIG_NO_VLAN
+int vlan_init(struct hostapd_data *hapd);
+void vlan_deinit(struct hostapd_data *hapd);
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+ int vlan_id,
+ struct vlan_description *vlan_desc);
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
+#else /* CONFIG_NO_VLAN */
+static inline int vlan_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void vlan_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline struct hostapd_vlan *
+vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ int vlan_id, struct vlan_description *vlan_desc)
+{
+ return NULL;
+}
+
+static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+ return -1;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_INIT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ioctl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ioctl.c
new file mode 100644
index 0000000..987b612
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ioctl.c
@@ -0,0 +1,155 @@
+/*
+ * hostapd / VLAN ioctl API
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "common/linux_vlan.h"
+#include "vlan_util.h"
+
+
+int vlan_rem(const char *if_name)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ if_name);
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&if_request, 0, sizeof(if_request));
+
+ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+ if_request.cmd = DEL_VLAN_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+ "%s", __func__, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add a vlan interface with VLAN ID 'vid' and tagged interface
+ 'if_name'.
+
+ returns -1 on error
+ returns 1 if the interface already exists
+ returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+ if_name, vid);
+ ifconfig_up(if_name);
+
+ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ if_name);
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&if_request, 0, sizeof(if_request));
+
+ /* Determine if a suitable vlan device already exists. */
+
+ os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+ vid);
+
+ if_request.cmd = GET_VLAN_VID_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+ if_request.u.VID == vid) {
+ if_request.cmd = GET_VLAN_REALDEV_NAME_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+ os_strncmp(if_request.u.device2, if_name,
+ sizeof(if_request.u.device2)) == 0) {
+ close(fd);
+ wpa_printf(MSG_DEBUG,
+ "VLAN: vlan_add: if_name %s exists already",
+ if_request.device1);
+ return 1;
+ }
+ }
+
+ /* A suitable vlan device does not already exist, add one. */
+
+ os_memset(&if_request, 0, sizeof(if_request));
+ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+ if_request.u.VID = vid;
+ if_request.cmd = ADD_VLAN_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ wpa_printf(MSG_ERROR,
+ "VLAN: %s: ADD_VLAN_CMD failed for %s: %s",
+ __func__, if_request.device1, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+ name_type);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR,
+ "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&if_request, 0, sizeof(if_request));
+
+ if_request.u.name_type = name_type;
+ if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ wpa_printf(MSG_ERROR,
+ "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s",
+ __func__, name_type, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.c
new file mode 100644
index 0000000..56d1d3d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.c
@@ -0,0 +1,174 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+#include "utils/common.h"
+#include "vlan_util.h"
+
+/*
+ * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
+ * tagged interface 'if_name'.
+ *
+ * returns -1 on error
+ * returns 1 if the interface already exists
+ * returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+ int err, ret = -1;
+ struct nl_sock *handle = NULL;
+ struct rtnl_link *rlink = NULL;
+ int if_idx = 0;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
+ "vlan_if_name=%s)", if_name, vid, vlan_if_name);
+
+ if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ if_name);
+ return -1;
+ }
+
+ if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ vlan_if_name);
+ return -1;
+ }
+
+ handle = nl_socket_alloc();
+ if (!handle) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+ goto vlan_add_error;
+ }
+
+ err = nl_connect(handle, NETLINK_ROUTE);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+ nl_geterror(err));
+ goto vlan_add_error;
+ }
+
+ err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
+ if (err < 0) {
+ /* link does not exist */
+ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
+ if_name);
+ goto vlan_add_error;
+ }
+ if_idx = rtnl_link_get_ifindex(rlink);
+ rtnl_link_put(rlink);
+ rlink = NULL;
+
+ err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink);
+ if (err >= 0) {
+ /* link does exist */
+ rtnl_link_put(rlink);
+ rlink = NULL;
+ wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
+ vlan_if_name);
+ ret = 1;
+ goto vlan_add_error;
+ }
+
+ rlink = rtnl_link_alloc();
+ if (!rlink) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
+ goto vlan_add_error;
+ }
+
+ err = rtnl_link_set_type(rlink, "vlan");
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s",
+ nl_geterror(err));
+ goto vlan_add_error;
+ }
+
+ rtnl_link_set_link(rlink, if_idx);
+ rtnl_link_set_name(rlink, vlan_if_name);
+
+ err = rtnl_link_vlan_set_id(rlink, vid);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s",
+ nl_geterror(err));
+ goto vlan_add_error;
+ }
+
+ err = rtnl_link_add(handle, rlink, NLM_F_CREATE);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
+ "vlan %d on %s (%d): %s",
+ vlan_if_name, vid, if_name, if_idx,
+ nl_geterror(err));
+ goto vlan_add_error;
+ }
+
+ ret = 0;
+
+vlan_add_error:
+ if (rlink)
+ rtnl_link_put(rlink);
+ if (handle)
+ nl_socket_free(handle);
+ return ret;
+}
+
+
+int vlan_rem(const char *if_name)
+{
+ int err, ret = -1;
+ struct nl_sock *handle = NULL;
+ struct rtnl_link *rlink = NULL;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
+
+ handle = nl_socket_alloc();
+ if (!handle) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+ goto vlan_rem_error;
+ }
+
+ err = nl_connect(handle, NETLINK_ROUTE);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+ nl_geterror(err));
+ goto vlan_rem_error;
+ }
+
+ err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
+ if (err < 0) {
+ /* link does not exist */
+ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
+ if_name);
+ goto vlan_rem_error;
+ }
+
+ err = rtnl_link_delete(handle, rlink);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s",
+ if_name, nl_geterror(err));
+ goto vlan_rem_error;
+ }
+
+ ret = 0;
+
+vlan_rem_error:
+ if (rlink)
+ rtnl_link_put(rlink);
+ if (handle)
+ nl_socket_free(handle);
+ return ret;
+}
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.h
new file mode 100644
index 0000000..2446859
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.h
@@ -0,0 +1,31 @@
+/*
+ * hostapd / VLAN netlink/ioctl api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_UTIL_H
+#define VLAN_UTIL_H
+
+struct hostapd_data;
+struct hostapd_vlan;
+struct full_dynamic_vlan;
+
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
+int vlan_rem(const char *if_name);
+int vlan_set_name_type(unsigned int name_type);
+
+int ifconfig_helper(const char *if_name, int up);
+int ifconfig_up(const char *if_name);
+int iface_exists(const char *ifname);
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd);
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv);
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd);
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd);
+
+#endif /* VLAN_UTIL_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.c
new file mode 100644
index 0000000..9ebb01e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.c
@@ -0,0 +1,392 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wmm.h"
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+
+static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
+{
+ u8 ret;
+ ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
+ if (acm)
+ ret |= WMM_AC_ACM;
+ ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
+ return ret;
+}
+
+
+static inline u8 wmm_ecw(int ecwmin, int ecwmax)
+{
+ return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
+ ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
+}
+
+
+static void
+wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf,
+ struct hostapd_wmm_ac_params *wmm,
+ const struct hostapd_wmm_rule *wmm_reg)
+{
+ int ac;
+
+ for (ac = 0; ac < WMM_AC_NUM; ac++) {
+ wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin);
+ wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax);
+ wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs);
+ wmm[ac].txop_limit =
+ MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop);
+ wmm[ac].admission_control_mandatory =
+ wmm_conf[ac].admission_control_mandatory;
+ }
+}
+
+
+/*
+ * Calculate WMM regulatory limit if any.
+ */
+static void wmm_calc_regulatory_limit(struct hostapd_data *hapd,
+ struct hostapd_wmm_ac_params *acp)
+{
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ int c;
+
+ os_memcpy(acp, hapd->iconf->wmm_ac_params,
+ sizeof(hapd->iconf->wmm_ac_params));
+
+ for (c = 0; mode && c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->freq != hapd->iface->freq)
+ continue;
+
+ if (chan->wmm_rules_valid)
+ wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params,
+ acp, chan->wmm_rules);
+ break;
+ }
+
+ /*
+ * Check if we need to update set count. Since both were initialized to
+ * zero we can compare the whole array in one shot.
+ */
+ if (os_memcmp(acp, hapd->iface->prev_wmm,
+ sizeof(hapd->iconf->wmm_ac_params)) != 0) {
+ os_memcpy(hapd->iface->prev_wmm, acp,
+ sizeof(hapd->iconf->wmm_ac_params));
+ hapd->parameter_set_count++;
+ }
+}
+
+
+/*
+ * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
+ * Response frames.
+ */
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ struct wmm_parameter_element *wmm =
+ (struct wmm_parameter_element *) (pos + 2);
+ struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM];
+ int e;
+
+ os_memset(wmmp, 0, sizeof(wmmp));
+
+ if (!hapd->conf->wmm_enabled)
+ return eid;
+ wmm_calc_regulatory_limit(hapd, wmmp);
+ eid[0] = WLAN_EID_VENDOR_SPECIFIC;
+ wmm->oui[0] = 0x00;
+ wmm->oui[1] = 0x50;
+ wmm->oui[2] = 0xf2;
+ wmm->oui_type = WMM_OUI_TYPE;
+ wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
+ wmm->version = WMM_VERSION;
+ wmm->qos_info = hapd->parameter_set_count & 0xf;
+
+ if (hapd->conf->wmm_uapsd &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+ wmm->qos_info |= 0x80;
+
+ wmm->reserved = 0;
+
+ /* fill in a parameter set record for each AC */
+ for (e = 0; e < 4; e++) {
+ struct wmm_ac_parameter *ac = &wmm->ac[e];
+ struct hostapd_wmm_ac_params *acp = &wmmp[e];
+
+ ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
+ acp->admission_control_mandatory,
+ e);
+ ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
+ ac->txop_limit = host_to_le16(acp->txop_limit);
+ }
+
+ pos = (u8 *) (wmm + 1);
+ eid[1] = pos - eid - 2; /* element length */
+
+ return pos;
+}
+
+
+/*
+ * This function is called when a station sends an association request with
+ * WMM info element. The function returns 1 on success or 0 on any error in WMM
+ * element. eid does not include Element ID and Length octets.
+ */
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
+{
+ struct wmm_information_element *wmm;
+
+ wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
+
+ if (len < sizeof(struct wmm_information_element)) {
+ wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
+ (unsigned long) len);
+ return 0;
+ }
+
+ wmm = (struct wmm_information_element *) eid;
+ wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x "
+ "OUI type %d OUI sub-type %d version %d QoS info 0x%x",
+ wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
+ wmm->oui_subtype, wmm->version, wmm->qos_info);
+ if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
+ wmm->version != WMM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
+ const struct wmm_tspec_element *tspec,
+ u8 action_code, u8 dialogue_token, u8 status_code)
+{
+ u8 buf[256];
+ struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
+ struct wmm_tspec_element *t = (struct wmm_tspec_element *)
+ m->u.action.u.wmm_action.variable;
+ int len;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "action response - reason %d", status_code);
+ os_memset(buf, 0, sizeof(buf));
+ m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(m->da, addr, ETH_ALEN);
+ os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+ m->u.action.category = WLAN_ACTION_WMM;
+ m->u.action.u.wmm_action.action_code = action_code;
+ m->u.action.u.wmm_action.dialog_token = dialogue_token;
+ m->u.action.u.wmm_action.status_code = status_code;
+ os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
+ len = ((u8 *) (t + 1)) - buf;
+
+ if (hostapd_drv_send_mlme(hapd, m, len, 0, NULL, 0, 0) < 0)
+ wpa_printf(MSG_INFO, "wmm_send_action: send failed");
+}
+
+
+int wmm_process_tspec(struct wmm_tspec_element *tspec)
+{
+ u64 medium_time;
+ unsigned int pps, duration;
+ unsigned int up, psb, dir, tid;
+ u16 val, surplus;
+
+ up = (tspec->ts_info[1] >> 3) & 0x07;
+ psb = (tspec->ts_info[1] >> 2) & 0x01;
+ dir = (tspec->ts_info[0] >> 5) & 0x03;
+ tid = (tspec->ts_info[0] >> 1) & 0x0f;
+ wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
+ up, psb, dir, tid);
+ val = le_to_host16(tspec->nominal_msdu_size);
+ wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
+ val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
+ wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
+ le_to_host32(tspec->mean_data_rate));
+ wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
+ le_to_host32(tspec->minimum_phy_rate));
+ val = le_to_host16(tspec->surplus_bandwidth_allowance);
+ wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
+ val >> 13, 10000 * (val & 0x1fff) / 0x2000);
+
+ val = le_to_host16(tspec->nominal_msdu_size);
+ if (val == 0) {
+ wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+ /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
+ pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
+ wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
+ pps);
+
+ if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
+ wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+
+ duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
+ (le_to_host32(tspec->minimum_phy_rate) / 1000000) +
+ 50 /* FIX: proper SIFS + ACK duration */;
+
+ /* unsigned binary number with an implicit binary point after the
+ * leftmost 3 bits, i.e., 0x2000 = 1.0 */
+ surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
+ if (surplus <= 0x2000) {
+ wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
+ "greater than unity");
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+
+ medium_time = (u64) surplus * pps * duration / 0x2000;
+ wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
+ (unsigned long) medium_time);
+
+ /*
+ * TODO: store list of granted (and still active) TSPECs and check
+ * whether there is available medium time for this request. For now,
+ * just refuse requests that would by themselves take very large
+ * portion of the available bandwidth.
+ */
+ if (medium_time > 750000) {
+ wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
+ "75%% of available bandwidth");
+ return WMM_ADDTS_STATUS_REFUSED;
+ }
+
+ /* Convert to 32 microseconds per second unit */
+ tspec->medium_time = host_to_le16(medium_time / 32);
+
+ return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
+}
+
+
+static void wmm_addts_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ const struct wmm_tspec_element *tspec, size_t len)
+{
+ const u8 *end = ((const u8 *) mgmt) + len;
+ int res;
+ struct wmm_tspec_element tspec_resp;
+
+ if ((const u8 *) (tspec + 1) > end) {
+ wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
+ "from " MACSTR,
+ mgmt->u.action.u.wmm_action.dialog_token,
+ MAC2STR(mgmt->sa));
+
+ os_memcpy(&tspec_resp, tspec, sizeof(struct wmm_tspec_element));
+ res = wmm_process_tspec(&tspec_resp);
+ wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
+
+ wmm_send_action(hapd, mgmt->sa, &tspec_resp, WMM_ACTION_CODE_ADDTS_RESP,
+ mgmt->u.action.u.wmm_action.dialog_token, res);
+}
+
+
+void hostapd_wmm_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ int action_code;
+ int left = len - IEEE80211_HDRLEN - 4;
+ const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
+ struct ieee802_11_elems elems;
+ struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+
+ /* check that the request comes from a valid station */
+ if (!sta ||
+ (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
+ (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "wmm action received is not from associated wmm"
+ " station");
+ /* TODO: respond with action frame refused status code */
+ return;
+ }
+
+ if (left < 0)
+ return; /* not a valid WMM Action frame */
+
+ /* extract the tspec info element */
+ if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "hostapd_wmm_action - could not parse wmm "
+ "action");
+ /* TODO: respond with action frame invalid parameters status
+ * code */
+ return;
+ }
+
+ if (!elems.wmm_tspec ||
+ elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "hostapd_wmm_action - missing or wrong length "
+ "tspec");
+ /* TODO: respond with action frame invalid parameters status
+ * code */
+ return;
+ }
+
+ /* TODO: check the request is for an AC with ACM set, if not, refuse
+ * request */
+
+ action_code = mgmt->u.action.u.wmm_action.action_code;
+ switch (action_code) {
+ case WMM_ACTION_CODE_ADDTS_REQ:
+ wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
+ (elems.wmm_tspec - 2), len);
+ return;
+#if 0
+ /* TODO: needed for client implementation */
+ case WMM_ACTION_CODE_ADDTS_RESP:
+ wmm_setup_request(hapd, mgmt, len);
+ return;
+ /* TODO: handle station teardown requests */
+ case WMM_ACTION_CODE_DELTS:
+ wmm_teardown(hapd, mgmt, len);
+ return;
+#endif
+ }
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "hostapd_wmm_action - unknown action code %d",
+ action_code);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.h
new file mode 100644
index 0000000..b70b863
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.h
@@ -0,0 +1,23 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WME_H
+#define WME_H
+
+struct ieee80211_mgmt;
+struct wmm_tspec_element;
+
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid);
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid,
+ size_t len);
+void hostapd_wmm_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int wmm_process_tspec(struct wmm_tspec_element *tspec);
+
+#endif /* WME_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.c
new file mode 100644
index 0000000..ba1dd2e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.c
@@ -0,0 +1,1059 @@
+/*
+ * hostapd - WNM
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/ocv.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/wpa_auth.h"
+#include "mbo_ap.h"
+#include "wnm_ap.h"
+
+#define MAX_TFS_IE_LEN 1024
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+ u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+ wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+ return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+ u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+ wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+ return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.response */
+static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
+ const u8 *addr, u8 dialog_token,
+ u8 action_type, u16 intval)
+{
+ struct ieee80211_mgmt *mgmt;
+ int res;
+ size_t len;
+ size_t gtk_elem_len = 0;
+ size_t igtk_elem_len = 0;
+ size_t bigtk_elem_len = 0;
+ struct wnm_sleep_element wnmsleep_ie;
+ u8 *wnmtfs_ie, *oci_ie;
+ u8 wnmsleep_ie_len, oci_ie_len;
+ u16 wnmtfs_ie_len;
+ u8 *pos;
+ struct sta_info *sta;
+ enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
+ WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+ return -EINVAL;
+ }
+
+ /* WNM-Sleep Mode IE */
+ os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
+ wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+ wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
+ wnmsleep_ie.len = wnmsleep_ie_len - 2;
+ wnmsleep_ie.action_type = action_type;
+ wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
+ wnmsleep_ie.intval = host_to_le16(intval);
+
+ /* TFS IE(s) */
+ wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+ if (wnmtfs_ie == NULL)
+ return -1;
+ if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
+ tfs_oper)) {
+ wnmtfs_ie_len = 0;
+ os_free(wnmtfs_ie);
+ wnmtfs_ie = NULL;
+ }
+
+ oci_ie = NULL;
+ oci_ie_len = 0;
+#ifdef CONFIG_OCV
+ if (action_type == WNM_SLEEP_MODE_EXIT &&
+ wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->oci_freq_override_wnm_sleep) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ hapd->conf->oci_freq_override_wnm_sleep);
+ ci.frequency = hapd->conf->oci_freq_override_wnm_sleep;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(wnmtfs_ie);
+ os_free(oci_ie);
+ return -1;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+#define MAX_GTK_SUBELEM_LEN 45
+#define MAX_IGTK_SUBELEM_LEN 26
+#define MAX_BIGTK_SUBELEM_LEN 26
+ mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+ MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
+ MAX_BIGTK_SUBELEM_LEN +
+ oci_ie_len);
+ if (mgmt == NULL) {
+ wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+ "WNM-Sleep Response action frame");
+ res = -1;
+ goto fail;
+ }
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
+ mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
+ pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
+ /* add key data if MFP is enabled */
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys ||
+ action_type != WNM_SLEEP_MODE_EXIT) {
+ mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
+ } else {
+ gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
+ pos += gtk_elem_len;
+ wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
+ (int) gtk_elem_len);
+ res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
+ if (res < 0)
+ goto fail;
+ igtk_elem_len = res;
+ pos += igtk_elem_len;
+ wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
+ (int) igtk_elem_len);
+ if (hapd->conf->beacon_prot &&
+ (hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_BEACON_PROTECTION)) {
+ res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos);
+ if (res < 0)
+ goto fail;
+ bigtk_elem_len = res;
+ pos += bigtk_elem_len;
+ wpa_printf(MSG_DEBUG, "Pass 4 bigtk_len = %d",
+ (int) bigtk_elem_len);
+ }
+
+ WPA_PUT_LE16((u8 *)
+ &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
+ gtk_elem_len + igtk_elem_len + bigtk_elem_len);
+ }
+ os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
+ /* copy TFS IE here */
+ pos += wnmsleep_ie_len;
+ if (wnmtfs_ie) {
+ os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+ pos += wnmtfs_ie_len;
+ }
+#ifdef CONFIG_OCV
+ /* copy OCV OCI here */
+ if (oci_ie_len > 0)
+ os_memcpy(pos, oci_ie, oci_ie_len);
+#endif /* CONFIG_OCV */
+
+ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
+ igtk_elem_len + bigtk_elem_len +
+ wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
+
+ /* In driver, response frame should be forced to sent when STA is in
+ * PS mode */
+ res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+ mgmt->da, &mgmt->u.action.category, len);
+
+ if (!res) {
+ wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
+ "frame");
+
+ /* when entering wnmsleep
+ * 1. pause the node in driver
+ * 2. mark the node so that AP won't update GTK/IGTK/BIGTK
+ * during WNM Sleep
+ */
+ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
+ wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
+ sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
+ hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
+ addr, NULL, NULL);
+ wpa_set_wnmsleep(sta->wpa_sm, 1);
+ }
+ /* when exiting wnmsleep
+ * 1. unmark the node
+ * 2. start GTK/IGTK/BIGTK update if MFP is not used
+ * 3. unpause the node in driver
+ */
+ if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
+ wnmsleep_ie.status ==
+ WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
+ wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+ wpa_set_wnmsleep(sta->wpa_sm, 0);
+ hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
+ addr, NULL, NULL);
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys)
+ wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
+ }
+ } else
+ wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
+
+#undef MAX_GTK_SUBELEM_LEN
+#undef MAX_IGTK_SUBELEM_LEN
+#undef MAX_BIGTK_SUBELEM_LEN
+fail:
+ os_free(wnmtfs_ie);
+ os_free(oci_ie);
+ os_free(mgmt);
+ return res;
+}
+
+
+static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm, int len)
+{
+ /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
+ const u8 *pos = frm;
+ u8 dialog_token;
+ struct wnm_sleep_element *wnmsleep_ie = NULL;
+ /* multiple TFS Req IE (assuming consecutive) */
+ u8 *tfsreq_ie_start = NULL;
+ u8 *tfsreq_ie_end = NULL;
+ u16 tfsreq_ie_len = 0;
+#ifdef CONFIG_OCV
+ struct sta_info *sta;
+ const u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
+#endif /* CONFIG_OCV */
+
+ if (!hapd->conf->wnm_sleep_mode) {
+ wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
+ MACSTR " since WNM-Sleep Mode is disabled",
+ MAC2STR(addr));
+ return;
+ }
+
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short WNM-Sleep Mode Request from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ dialog_token = *pos++;
+ while (pos + 1 < frm + len) {
+ u8 ie_len = pos[1];
+ if (pos + 2 + ie_len > frm + len)
+ break;
+ if (*pos == WLAN_EID_WNMSLEEP &&
+ ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
+ wnmsleep_ie = (struct wnm_sleep_element *) pos;
+ else if (*pos == WLAN_EID_TFS_REQ) {
+ if (!tfsreq_ie_start)
+ tfsreq_ie_start = (u8 *) pos;
+ tfsreq_ie_end = (u8 *) pos;
+#ifdef CONFIG_OCV
+ } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
+ pos[2] == WLAN_EID_EXT_OCV_OCI) {
+ oci_ie = pos + 3;
+ oci_ie_len = ie_len - 1;
+#endif /* CONFIG_OCV */
+ } else
+ wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+ *pos);
+ pos += ie_len + 2;
+ }
+
+ if (!wnmsleep_ie) {
+ wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+ return;
+ }
+
+#ifdef CONFIG_OCV
+ sta = ap_get_sta(hapd, addr);
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
+ sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
+ return;
+ }
+
+ if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(hapd, MSG_WARNING, "WNM: OCV failed: %s",
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+ tfsreq_ie_start && tfsreq_ie_end &&
+ tfsreq_ie_end - tfsreq_ie_start >= 0) {
+ tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
+ tfsreq_ie_start;
+ wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
+ /* pass the TFS Req IE(s) to driver for processing */
+ if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+ &tfsreq_ie_len,
+ WNM_SLEEP_TFS_REQ_IE_SET))
+ wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
+ }
+
+ ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
+ wnmsleep_ie->action_type,
+ le_to_host16(wnmsleep_ie->intval));
+
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+ /* clear the tfs after sending the resp frame */
+ ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+ &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
+ }
+}
+
+
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ const u8 *addr,
+ u8 dialog_token)
+{
+ struct ieee80211_mgmt *mgmt;
+ size_t len;
+ u8 *pos;
+ int res;
+
+ mgmt = os_zalloc(sizeof(*mgmt));
+ if (mgmt == NULL)
+ return -1;
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+ mgmt->u.action.u.bss_tm_req.req_mode = 0;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+ MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
+ "validity_interval=%u",
+ MAC2STR(addr), dialog_token,
+ mgmt->u.action.u.bss_tm_req.req_mode,
+ le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
+ mgmt->u.action.u.bss_tm_req.validity_interval);
+
+ len = pos - &mgmt->u.action.category;
+ res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+ mgmt->da, &mgmt->u.action.category, len);
+ os_free(mgmt);
+ return res;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm,
+ size_t len)
+{
+ u8 dialog_token, reason;
+ const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+ char *hex = NULL;
+ size_t hex_len;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Query from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
+
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ pos = frm;
+ end = pos + len;
+ dialog_token = *pos++;
+ reason = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
+ MACSTR " dialog_token=%u reason=%u",
+ MAC2STR(addr), dialog_token, reason);
+
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+
+ hex_len = 2 * (end - pos) + 1;
+ if (hex_len > 1) {
+ hex = os_malloc(hex_len);
+ if (hex)
+ wpa_snprintf_hex(hex, hex_len, pos, end - pos);
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ BSS_TM_QUERY MACSTR " reason=%u%s%s",
+ MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+ os_free(hex);
+
+ if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
+ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+}
+
+
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (sta->agreed_to_steer) {
+ wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->agreed_to_steer = 0;
+ }
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm,
+ size_t len)
+{
+ u8 dialog_token, status_code, bss_termination_delay;
+ const u8 *pos, *end, *target_bssid = NULL;
+ int enabled = hapd->conf->bss_transition;
+ struct sta_info *sta;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Response from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
+
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ pos = frm;
+ end = pos + len;
+ dialog_token = *pos++;
+ status_code = *pos++;
+ bss_termination_delay = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
+ MACSTR " dialog_token=%u status_code=%u "
+ "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
+ status_code, bss_termination_delay);
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for received BSS TM Response",
+ MAC2STR(addr));
+ return;
+ }
+
+ if (status_code == WNM_BSS_TM_ACCEPT) {
+ if (end - pos < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ return;
+ }
+ target_bssid = pos;
+ sta->agreed_to_steer = 1;
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+ hapd, sta);
+ wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
+ MAC2STR(pos));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+ " status_code=%u bss_termination_delay=%u target_bssid="
+ MACSTR,
+ MAC2STR(addr), status_code, bss_termination_delay,
+ MAC2STR(pos));
+ pos += ETH_ALEN;
+ } else {
+ sta->agreed_to_steer = 0;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+ " status_code=%u bss_termination_delay=%u",
+ MAC2STR(addr), status_code, bss_termination_delay);
+ }
+
+ hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
+ status_code, bss_termination_delay,
+ target_bssid, pos, end - pos);
+
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+}
+
+
+static void wnm_beacon_protection_failure(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ struct sta_info *sta;
+
+ if (!hapd->conf->beacon_prot ||
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+ return;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for received WNM-Notification Request",
+ MAC2STR(addr));
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Beacon protection failure reported");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_UNPROT_BEACON "reporter="
+ MACSTR, MAC2STR(addr));
+}
+
+
+static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *buf,
+ size_t len)
+{
+ u8 dialog_token, type;
+
+ if (len < 2)
+ return;
+ dialog_token = *buf++;
+ type = *buf++;
+ len -= 2;
+
+ wpa_printf(MSG_DEBUG,
+ "WNM: Received WNM Notification Request frame from "
+ MACSTR " (dialog_token=%u type=%u)",
+ MAC2STR(addr), dialog_token, type);
+ wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
+ buf, len);
+ switch (type) {
+ case WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE:
+ wnm_beacon_protection_failure(hapd, addr);
+ break;
+ case WNM_NOTIF_TYPE_VENDOR_SPECIFIC:
+ mbo_ap_wnm_notification_req(hapd, addr, buf, len);
+ break;
+ }
+}
+
+
+static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *buf,
+ size_t len)
+{
+ u8 dialog_token;
+ char *hex;
+ size_t hex_len;
+
+ if (!hapd->conf->coloc_intf_reporting) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore unexpected Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ dialog_token = *buf++;
+ len--;
+
+ wpa_printf(MSG_DEBUG,
+ "WNM: Received Collocated Interference Report frame from "
+ MACSTR " (dialog_token=%u)",
+ MAC2STR(addr), dialog_token);
+ wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
+ buf, len);
+
+ hex_len = 2 * len + 1;
+ hex = os_malloc(hex_len);
+ if (!hex)
+ return;
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
+ MAC2STR(addr), dialog_token, hex);
+ os_free(hex);
+}
+
+
+
+static const char * wnm_event_type2str(enum wnm_event_report_type wtype)
+{
+#define W2S(wtype) case WNM_EVENT_TYPE_ ## wtype: return #wtype;
+ switch (wtype) {
+ W2S(TRANSITION)
+ W2S(RSNA)
+ W2S(P2P_LINK)
+ W2S(WNM_LOG)
+ W2S(BSS_COLOR_COLLISION)
+ W2S(BSS_COLOR_IN_USE)
+ }
+ return "UNKNOWN";
+#undef W2S
+}
+
+
+static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *buf,
+ size_t len)
+{
+ struct sta_info *sta;
+ u8 dialog_token;
+ struct wnm_event_report_element *report_ie;
+ const u8 *pos = buf, *end = buf + len;
+ const size_t fixed_field_len = 3; /* Event Token/Type/Report Status */
+#ifdef CONFIG_IEEE80211AX
+ const size_t tsf_len = 8;
+ u8 color;
+ u64 bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
+ if (end - pos < 1 + 2) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short WNM Event Report frame from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ dialog_token = *pos++;
+ report_ie = (struct wnm_event_report_element *) pos;
+
+ if (end - pos < 2 + report_ie->len ||
+ report_ie->len < fixed_field_len) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore truncated WNM Event Report frame from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ if (report_ie->eid != WLAN_EID_EVENT_REPORT ||
+ report_ie->status != WNM_STATUS_SUCCESSFUL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WNM: Received WNM Event Report frame from "
+ MACSTR " dialog_token=%u event_token=%u type=%d (%s)",
+ MAC2STR(addr), dialog_token, report_ie->token,
+ report_ie->type, wnm_event_type2str(report_ie->type));
+
+ pos += 2 + fixed_field_len;
+ wpa_hexdump(MSG_MSGDUMP, "WNM: Event Report", pos, end - pos);
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for received WNM Event Report",
+ MAC2STR(addr));
+ return;
+ }
+
+ switch (report_ie->type) {
+#ifdef CONFIG_IEEE80211AX
+ case WNM_EVENT_TYPE_BSS_COLOR_COLLISION:
+ if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ return;
+ if (report_ie->len <
+ fixed_field_len + tsf_len + 8) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Too short BSS color collision event report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ bitmap = WPA_GET_LE64(report_ie->u.bss_color_collision.color_bitmap);
+ wpa_printf(MSG_DEBUG,
+ "WNM: BSS color collision bitmap 0x%llx reported by "
+ MACSTR, (unsigned long long) bitmap, MAC2STR(addr));
+ hostapd_switch_color(hapd->iface->bss[0], bitmap);
+ break;
+ case WNM_EVENT_TYPE_BSS_COLOR_IN_USE:
+ if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ return;
+ if (report_ie->len < fixed_field_len + tsf_len + 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Too short BSS color in use event report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ color = report_ie->u.bss_color_in_use.color;
+ if (color > 63) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Invalid BSS color %u report from "
+ MACSTR, color, MAC2STR(addr));
+ return;
+ }
+ if (color == 0) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: BSS color use report canceled by "
+ MACSTR, MAC2STR(addr));
+ /* TODO: Clear stored color from the collision bitmap
+ * if there are no other users for it. */
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WNM: BSS color %u use report by "
+ MACSTR, color, MAC2STR(addr));
+ hapd->color_collision_bitmap |= 1ULL << color;
+ break;
+#endif /* CONFIG_IEEE80211AX */
+ default:
+ wpa_printf(MSG_DEBUG,
+ "WNM Event Report type=%d (%s) not supported",
+ report_ie->type,
+ wnm_event_type2str(report_ie->type));
+ break;
+ }
+}
+
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ u8 action;
+ const u8 *payload;
+ size_t plen;
+
+ if (len < IEEE80211_HDRLEN + 2)
+ return -1;
+
+ payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+ action = *payload++;
+ plen = len - IEEE80211_HDRLEN - 2;
+
+ switch (action) {
+ case WNM_EVENT_REPORT:
+ ieee802_11_rx_wnm_event_report(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ case WNM_BSS_TRANS_MGMT_QUERY:
+ hapd->openwrt_stats.wnm.bss_transition_query_rx++;
+ ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ case WNM_BSS_TRANS_MGMT_RESP:
+ hapd->openwrt_stats.wnm.bss_transition_response_rx++;
+ ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ case WNM_SLEEP_MODE_REQ:
+ ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
+ return 0;
+ case WNM_NOTIFICATION_REQ:
+ ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ case WNM_COLLOCATED_INTERFERENCE_REPORT:
+ ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+ action, MAC2STR(mgmt->sa));
+ return -1;
+}
+
+
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, int disassoc_timer)
+{
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode =
+ WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+ MACSTR, disassoc_timer, MAC2STR(sta->addr));
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+ "Management Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+ int disassoc_timer)
+{
+ int timeout, beacon_int;
+
+ /*
+ * Prevent STA from reconnecting using cached PMKSA to force
+ * full authentication with the authentication server (which may
+ * decide to reject the connection),
+ */
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+ beacon_int = hapd->iconf->beacon_int;
+ if (beacon_int < 1)
+ beacon_int = 100; /* best guess */
+ /* Calculate timeout in ms based on beacon_int in TU */
+ timeout = disassoc_timer * beacon_int * 128 / 125;
+ wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+ " set to %d ms", MAC2STR(sta->addr), timeout);
+
+ sta->timeout_next = STA_DISASSOC_FROM_CLI;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(timeout / 1000,
+ timeout % 1000 * 1000,
+ ap_handle_timer, hapd, sta);
+}
+
+
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, const char *url,
+ int disassoc_timer)
+{
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t url_len;
+
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode =
+ WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
+ WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ /* Session Information URL */
+ url_len = os_strlen(url);
+ if (url_len > 255)
+ return -1;
+ *pos++ = url_len;
+ os_memcpy(pos, url, url_len);
+ pos += url_len;
+
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+ "Management Request frame");
+ return -1;
+ }
+
+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ if (disassoc_timer) {
+ /* send disassociation frame after time-out */
+ set_disassoc_timer(hapd, sta, disassoc_timer);
+ }
+
+ return 0;
+}
+
+
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 req_mode, int disassoc_timer, u8 valid_int,
+ const u8 *bss_term_dur, u8 dialog_token,
+ const char *url, const u8 *nei_rep, size_t nei_rep_len,
+ const u8 *mbo_attrs, size_t mbo_len)
+{
+ u8 *buf, *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t url_len;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+ MACSTR
+ " req_mode=0x%x disassoc_timer=%d valid_int=0x%x dialog_token=%u",
+ MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int,
+ dialog_token);
+ buf = os_zalloc(1000 + nei_rep_len + mbo_len);
+ if (buf == NULL)
+ return -1;
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+ mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+ bss_term_dur) {
+ os_memcpy(pos, bss_term_dur, 12);
+ pos += 12;
+ }
+
+ if (url) {
+ /* Session Information URL */
+ url_len = os_strlen(url);
+ if (url_len > 255) {
+ os_free(buf);
+ return -1;
+ }
+
+ *pos++ = url_len;
+ os_memcpy(pos, url, url_len);
+ pos += url_len;
+ }
+
+ if (nei_rep) {
+ os_memcpy(pos, nei_rep, nei_rep_len);
+ pos += nei_rep_len;
+ }
+
+ if (mbo_len > 0) {
+ pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
+ mbo_len);
+ }
+
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to send BSS Transition Management Request frame");
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+
+ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+ if (disassoc_timer) {
+ /* send disassociation frame after time-out */
+ set_disassoc_timer(hapd, sta, disassoc_timer);
+ }
+
+ return 0;
+}
+
+
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout)
+{
+ u8 buf[100], *pos;
+ struct ieee80211_mgmt *mgmt;
+ u8 dialog_token = 1;
+
+ if (auto_report > 3 || timeout > 63)
+ return -1;
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.coloc_intf_req.action =
+ WNM_COLLOCATED_INTERFERENCE_REQ;
+ mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
+ mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
+ pos = &mgmt->u.action.u.coloc_intf_req.req_info;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
+ MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
+ MAC2STR(sta->addr), dialog_token, auto_report, timeout);
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Failed to send Collocated Interference Request frame");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.h
new file mode 100644
index 0000000..f86c6b2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.h
@@ -0,0 +1,30 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_AP_H
+#define WNM_AP_H
+
+struct sta_info;
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, int disassoc_timer);
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, const char *url,
+ int disassoc_timer);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 req_mode, int disassoc_timer, u8 valid_int,
+ const u8 *bss_term_dur, u8 dialog_token,
+ const char *url, const u8 *nei_rep, size_t nei_rep_len,
+ const u8 *mbo_attrs, size_t mbo_len);
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout);
+
+#endif /* WNM_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.c
new file mode 100644
index 0000000..88f42a0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.c
@@ -0,0 +1,6649 @@
+/*
+ * IEEE 802.11 RSN / WPA Authenticator
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "utils/bitfield.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
+#include "common/dpp.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "crypto/random.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "drivers/driver.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+#define STATE_MACHINE_DATA struct wpa_state_machine
+#define STATE_MACHINE_DEBUG_PREFIX "WPA"
+#define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_sm_step(struct wpa_state_machine *sm);
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+ u8 *data, size_t data_len);
+#ifdef CONFIG_FILS
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *buf, size_t buf_len, u16 *_key_data_len);
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+ const struct wpabuf *hlp);
+#endif /* CONFIG_FILS */
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+static void wpa_request_new_ptk(struct wpa_state_machine *sm);
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+ const u8 *pmk, unsigned int pmk_len,
+ struct wpa_ptk *ptk, int force_sha256,
+ u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+ size_t *key_len);
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+static int ieee80211w_kde_len(struct wpa_state_machine *sm);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
+
+static const u32 eapol_key_timeout_first = 100; /* ms */
+static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+static const u32 eapol_key_timeout_first_group = 500; /* ms */
+static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+
+
+static const u8 * wpa_auth_get_aa(const struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ return sm->own_mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+ return sm->wpa_auth->addr;
+}
+
+
+static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ return sm->peer_mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+ return sm->addr;
+}
+
+
+static inline int wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+ if (wpa_auth->cb->mic_failure_report)
+ return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
+ return 0;
+}
+
+
+static inline void wpa_auth_psk_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+ if (wpa_auth->cb->psk_failure_report)
+ wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
+}
+
+
+static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, wpa_eapol_variable var,
+ int value)
+{
+ if (wpa_auth->cb->set_eapol)
+ wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
+}
+
+
+static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, wpa_eapol_variable var)
+{
+ if (!wpa_auth->cb->get_eapol)
+ return -1;
+ return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
+}
+
+
+static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr,
+ const u8 *p2p_dev_addr,
+ const u8 *prev_psk, size_t *psk_len,
+ int *vlan_id)
+{
+ if (!wpa_auth->cb->get_psk)
+ return NULL;
+ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+ prev_psk, psk_len, vlan_id);
+}
+
+
+static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, u8 *msk, size_t *len)
+{
+ if (!wpa_auth->cb->get_msk)
+ return -1;
+ return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+ int vlan_id,
+ enum wpa_alg alg, const u8 *addr, int idx,
+ u8 *key, size_t key_len,
+ enum key_flag key_flag)
+{
+ if (!wpa_auth->cb->set_key)
+ return -1;
+ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+ key, key_len, key_flag);
+}
+
+
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+ const u8 *peer_addr,
+ const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len)
+{
+ if (!wpa_auth->cb->set_ltf_keyseed)
+ return -1;
+ return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+ ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+{
+ int res;
+
+ if (!wpa_auth->cb->get_seqnum)
+ return -1;
+ res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: Override GTK RSC %016llx --> %016llx",
+ (long long unsigned) WPA_GET_LE64(seq),
+ (long long unsigned)
+ WPA_GET_LE64(wpa_auth->conf.gtk_rsc_override));
+ os_memcpy(seq, wpa_auth->conf.gtk_rsc_override,
+ WPA_KEY_RSC_LEN);
+ }
+ if (!addr && idx >= 4 && idx <= 5 &&
+ wpa_auth->conf.igtk_rsc_override_set) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: Override IGTK RSC %016llx --> %016llx",
+ (long long unsigned) WPA_GET_LE64(seq),
+ (long long unsigned)
+ WPA_GET_LE64(wpa_auth->conf.igtk_rsc_override));
+ os_memcpy(seq, wpa_auth->conf.igtk_rsc_override,
+ WPA_KEY_RSC_LEN);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ return res;
+}
+
+
+static inline int
+wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *data, size_t data_len, int encrypt)
+{
+ if (!wpa_auth->cb->send_eapol)
+ return -1;
+ return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
+ encrypt);
+}
+
+
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+ const u8 *addr)
+{
+ if (!wpa_auth->cb->start_ampe)
+ return -1;
+ return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_state_machine *sm, void *ctx),
+ void *cb_ctx)
+{
+ if (!wpa_auth->cb->for_each_sta)
+ return 0;
+ return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
+}
+
+
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_authenticator *a, void *ctx),
+ void *cb_ctx)
+{
+ if (!wpa_auth->cb->for_each_auth)
+ return 0;
+ return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx);
+}
+
+
+void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int cipher,
+ u32 life_time, const struct wpa_ptk *ptk)
+{
+ if (wpa_auth->cb->store_ptksa)
+ wpa_auth->cb->store_ptksa(wpa_auth->cb_ctx, addr, cipher,
+ life_time, ptk);
+}
+
+
+static void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int cipher)
+{
+ if (wpa_auth->cb->clear_ptksa)
+ wpa_auth->cb->clear_ptksa(wpa_auth->cb_ctx, addr, cipher);
+}
+
+
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *txt)
+{
+ if (!wpa_auth->cb->logger)
+ return;
+ wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
+}
+
+
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *fmt, ...)
+{
+ char *format;
+ int maxlen;
+ va_list ap;
+
+ if (!wpa_auth->cb->logger)
+ return;
+
+ maxlen = os_strlen(fmt) + 100;
+ format = os_malloc(maxlen);
+ if (!format)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(format, maxlen, fmt, ap);
+ va_end(ap);
+
+ wpa_auth_logger(wpa_auth, addr, level, format);
+
+ os_free(format);
+}
+
+
+static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, u16 reason)
+{
+ if (!wpa_auth->cb->disconnect)
+ return;
+ wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
+ MAC2STR(addr), reason);
+ wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
+}
+
+
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_channel_info *ci)
+{
+ if (!wpa_auth->cb->channel_info)
+ return -1;
+ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
+static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int vlan_id)
+{
+ if (!wpa_auth->cb->update_vlan)
+ return -1;
+ return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
+}
+
+
+static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+ if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to get random data for WPA initialization.");
+ } else {
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+ wpa_hexdump_key(MSG_DEBUG, "GMK",
+ wpa_auth->group->GMK, WPA_GMK_LEN);
+ }
+
+ if (wpa_auth->conf.wpa_gmk_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+ wpa_rekey_gmk, wpa_auth, NULL);
+ }
+}
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct wpa_group *group, *next;
+
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+ group = wpa_auth->group;
+ while (group) {
+ wpa_group_get(wpa_auth, group);
+
+ group->GTKReKey = true;
+ do {
+ group->changed = false;
+ wpa_group_sm_step(wpa_auth, group);
+ } while (group->changed);
+
+ next = group->next;
+ wpa_group_put(wpa_auth, group);
+ group = next;
+ }
+
+ if (wpa_auth->conf.wpa_group_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
+ 0, wpa_rekey_gtk, wpa_auth, NULL);
+ }
+}
+
+
+static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct wpa_state_machine *sm = timeout_ctx;
+
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "rekeying PTK");
+ wpa_request_new_ptk(sm);
+ wpa_sm_step(sm);
+}
+
+
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
+{
+ if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
+ wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
+ MACSTR " (%d seconds)",
+ MAC2STR(wpa_auth_get_spa(sm)),
+ sm->wpa_auth->conf.wpa_ptk_rekey);
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
+ wpa_rekey_ptk, sm->wpa_auth, sm);
+ }
+}
+
+
+static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
+{
+ if (sm->pmksa == ctx)
+ sm->pmksa = NULL;
+ return 0;
+}
+
+
+static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx)
+{
+ struct wpa_authenticator *wpa_auth = ctx;
+ wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
+}
+
+
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
+ u8 rkey[32];
+ unsigned long ptr;
+
+ if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
+
+ /*
+ * Counter = PRF-256(Random number, "Init Counter",
+ * Local MAC Address || Time)
+ */
+ os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+ wpa_get_ntp_timestamp(buf + ETH_ALEN);
+ ptr = (unsigned long) group;
+ os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
+#ifdef TEST_FUZZ
+ os_memset(buf + ETH_ALEN, 0xab, 8);
+ os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr));
+#endif /* TEST_FUZZ */
+ if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+ return -1;
+
+ if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+ group->Counter, WPA_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+ group->Counter, WPA_NONCE_LEN);
+
+ return 0;
+}
+
+
+static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
+ int vlan_id, int delay_init)
+{
+ struct wpa_group *group;
+
+ group = os_zalloc(sizeof(struct wpa_group));
+ if (!group)
+ return NULL;
+
+ group->GTKAuthenticator = true;
+ group->vlan_id = vlan_id;
+ group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
+
+ if (random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO,
+ "WPA: Not enough entropy in random pool for secure operations - update keys later when the first station connects");
+ }
+
+ /*
+ * Set initial GMK/Counter value here. The actual values that will be
+ * used in negotiations will be set once the first station tries to
+ * connect. This allows more time for collecting additional randomness
+ * on embedded devices.
+ */
+ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Failed to get random data for WPA initialization.");
+ os_free(group);
+ return NULL;
+ }
+
+ group->GInit = true;
+ if (delay_init) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Delay group state machine start until Beacon frames have been configured");
+ /* Initialization is completed in wpa_init_keys(). */
+ } else {
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = false;
+ wpa_group_sm_step(wpa_auth, group);
+ }
+
+ return group;
+}
+
+
+/**
+ * wpa_init - Initialize WPA authenticator
+ * @addr: Authenticator address
+ * @conf: Configuration for WPA authenticator
+ * @cb: Callback functions for WPA authenticator
+ * Returns: Pointer to WPA authenticator data or %NULL on failure
+ */
+struct wpa_authenticator * wpa_init(const u8 *addr,
+ struct wpa_auth_config *conf,
+ const struct wpa_auth_callbacks *cb,
+ void *cb_ctx)
+{
+ struct wpa_authenticator *wpa_auth;
+
+ wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+ if (!wpa_auth)
+ return NULL;
+ os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+ wpa_auth->cb = cb;
+ wpa_auth->cb_ctx = cb_ctx;
+
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+ os_free(wpa_auth);
+ return NULL;
+ }
+
+ wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
+ if (!wpa_auth->group) {
+ os_free(wpa_auth->wpa_ie);
+ os_free(wpa_auth);
+ return NULL;
+ }
+
+ wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
+ wpa_auth);
+ if (!wpa_auth->pmksa) {
+ wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+ os_free(wpa_auth->group);
+ os_free(wpa_auth->wpa_ie);
+ os_free(wpa_auth);
+ return NULL;
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
+ if (!wpa_auth->ft_pmk_cache) {
+ wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+ os_free(wpa_auth->group);
+ os_free(wpa_auth->wpa_ie);
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+ os_free(wpa_auth);
+ return NULL;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if (wpa_auth->conf.wpa_gmk_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+ wpa_rekey_gmk, wpa_auth, NULL);
+ }
+
+ if (wpa_auth->conf.wpa_group_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+ wpa_rekey_gtk, wpa_auth, NULL);
+ }
+
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(conf->ip_addr_start)) {
+ int count = WPA_GET_BE32(conf->ip_addr_end) -
+ WPA_GET_BE32(conf->ip_addr_start) + 1;
+ if (count > 1000)
+ count = 1000;
+ if (count > 0)
+ wpa_auth->ip_pool = bitfield_alloc(count);
+ }
+#endif /* CONFIG_P2P */
+
+ return wpa_auth;
+}
+
+
+int wpa_init_keys(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group = wpa_auth->group;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Start group state machine to set initial keys");
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = false;
+ wpa_group_sm_step(wpa_auth, group);
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return -1;
+ return 0;
+}
+
+
+/**
+ * wpa_deinit - Deinitialize WPA authenticator
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ */
+void wpa_deinit(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group, *prev;
+
+ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
+ wpa_auth->ft_pmk_cache = NULL;
+ wpa_ft_deinit(wpa_auth);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_P2P
+ bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
+ os_free(wpa_auth->wpa_ie);
+
+ group = wpa_auth->group;
+ while (group) {
+ prev = group;
+ group = group->next;
+ bin_clear_free(prev, sizeof(*prev));
+ }
+
+ os_free(wpa_auth);
+}
+
+
+/**
+ * wpa_reconfig - Update WPA authenticator configuration
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ * @conf: Configuration for WPA authenticator
+ */
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_config *conf)
+{
+ struct wpa_group *group;
+
+ if (!wpa_auth)
+ return 0;
+
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+ return -1;
+ }
+
+ /*
+ * Reinitialize GTK to make sure it is suitable for the new
+ * configuration.
+ */
+ group = wpa_auth->group;
+ group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
+ group->GInit = true;
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = false;
+ wpa_group_sm_step(wpa_auth, group);
+
+ return 0;
+}
+
+
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr)
+{
+ struct wpa_state_machine *sm;
+
+ if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return NULL;
+
+ sm = os_zalloc(sizeof(struct wpa_state_machine));
+ if (!sm)
+ return NULL;
+ os_memcpy(sm->addr, addr, ETH_ALEN);
+ if (p2p_dev_addr)
+ os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+
+ sm->wpa_auth = wpa_auth;
+ sm->group = wpa_auth->group;
+ wpa_group_get(sm->wpa_auth, sm->group);
+#ifdef CONFIG_IEEE80211BE
+ sm->mld_assoc_link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
+
+ return sm;
+}
+
+
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm)
+{
+ if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
+ return -1;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (sm->ft_completed) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "FT authentication already completed - do not start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = true;
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sm->fils_completed) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "FILS authentication already completed - do not start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = true;
+ return 0;
+ }
+#endif /* CONFIG_FILS */
+
+ if (sm->started) {
+ os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+ sm->ReAuthenticationRequest = true;
+ return wpa_sm_step(sm);
+ }
+
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "start authentication");
+ sm->started = 1;
+
+ sm->Init = true;
+ if (wpa_sm_step(sm) == 1)
+ return 1; /* should not really happen */
+ sm->Init = false;
+ sm->AuthenticationRequest = true;
+ return wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+{
+ /* WPA/RSN was not used - clear WPA state. This is needed if the STA
+ * reassociates back to the same AP while the previous entry for the
+ * STA has not yet been removed. */
+ if (!sm)
+ return;
+
+ sm->wpa_key_mgmt = 0;
+}
+
+
+static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Free assigned IP address %u.%u.%u.%u from "
+ MACSTR " (bit %u)",
+ sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(wpa_auth_get_spa(sm)),
+ sm->ip_addr_bit);
+ bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
+ }
+#endif /* CONFIG_P2P */
+ if (sm->GUpdateStationKeys) {
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ os_free(sm->assoc_resp_ftie);
+ wpabuf_free(sm->ft_pending_req_ies);
+#endif /* CONFIG_IEEE80211R_AP */
+ os_free(sm->last_rx_eapol_key);
+ os_free(sm->wpa_ie);
+ os_free(sm->rsnxe);
+ wpa_group_put(sm->wpa_auth, sm->group);
+#ifdef CONFIG_DPP2
+ wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
+ bin_clear_free(sm, sizeof(*sm));
+}
+
+
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+{
+ struct wpa_authenticator *wpa_auth;
+
+ if (!sm)
+ return;
+
+ wpa_auth = sm->wpa_auth;
+ if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "strict rekeying - force GTK rekey since STA is leaving");
+ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+ wpa_auth, NULL) == -1)
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk,
+ wpa_auth, NULL);
+ }
+
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
+ eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
+ eloop_cancel_timeout(wpa_rekey_ptk, wpa_auth, sm);
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_sta_deinit(sm);
+#endif /* CONFIG_IEEE80211R_AP */
+ if (sm->in_step_loop) {
+ /* Must not free state machine while wpa_sm_step() is running.
+ * Freeing will be completed in the end of wpa_sm_step(). */
+ wpa_printf(MSG_DEBUG,
+ "WPA: Registering pending STA state machine deinit for "
+ MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
+ sm->pending_deinit = 1;
+ } else
+ wpa_free_sta_sm(sm);
+}
+
+
+static void wpa_request_new_ptk(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return;
+
+ if (!sm->use_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+ wpa_printf(MSG_INFO,
+ "WPA: PTK0 rekey not allowed, disconnect " MACSTR,
+ MAC2STR(wpa_auth_get_spa(sm)));
+ sm->Disconnect = true;
+ /* Try to encourage the STA to reconnect */
+ sm->disconnect_reason =
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+ } else {
+ if (sm->use_ext_key_id)
+ sm->keyidx_active ^= 1; /* flip Key ID */
+ sm->PTKRequest = true;
+ sm->PTK_valid = 0;
+ }
+}
+
+
+static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr,
+ const u8 *replay_counter)
+{
+ int i;
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!ctr[i].valid)
+ break;
+ if (os_memcmp(replay_counter, ctr[i].counter,
+ WPA_REPLAY_COUNTER_LEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
+ const u8 *replay_counter)
+{
+ int i;
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (ctr[i].valid &&
+ (!replay_counter ||
+ os_memcmp(replay_counter, ctr[i].counter,
+ WPA_REPLAY_COUNTER_LEN) == 0))
+ ctr[i].valid = false;
+ }
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ struct wpa_eapol_ie_parse *kde)
+{
+ struct wpa_ie_data ie;
+ struct rsn_mdie *mdie;
+
+ if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+ ie.num_pmkid != 1 || !ie.pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMKR1Name in FT 4-way handshake message 2/4");
+ return -1;
+ }
+
+ os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+ sm->sup_pmk_r1_name, PMKID_LEN);
+
+ if (!kde->mdie || !kde->ftie) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No %s in FT 4-way handshake message 2/4",
+ kde->mdie ? "FTIE" : "MDIE");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) (kde->mdie + 2);
+ if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+ os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+ return -1;
+ }
+
+ if (sm->assoc_resp_ftie &&
+ (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+ os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]) != 0)) {
+ wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+ kde->ftie, kde->ftie_len);
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+ sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int group)
+{
+ /* Supplicant reported a Michael MIC error */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "received EAPOL-Key Error Request (STA detected Michael MIC failure (group=%d))",
+ group);
+
+ if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "ignore Michael MIC failure report since group cipher is not TKIP");
+ } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "ignore Michael MIC failure report since pairwise cipher is not TKIP");
+ } else {
+ if (wpa_auth_mic_failure_report(wpa_auth,
+ wpa_auth_get_spa(sm)) > 0)
+ return 1; /* STA entry was removed */
+ sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+ wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+ }
+
+ /*
+ * Error report is not a request for a new key handshake, but since
+ * Authenticator may do it, let's change the keys now anyway.
+ */
+ wpa_request_new_ptk(sm);
+ return 0;
+}
+
+
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+ size_t data_len)
+{
+ struct wpa_ptk PTK;
+ int ok = 0;
+ const u8 *pmk = NULL;
+ size_t pmk_len;
+ int vlan_id = 0;
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t key_len;
+ int ret = -1;
+
+ os_memset(&PTK, 0, sizeof(PTK));
+ for (;;) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+ sm->p2p_dev_addr, pmk, &pmk_len,
+ &vlan_id);
+ if (!pmk)
+ break;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ } else {
+ pmk = sm->PMK;
+ pmk_len = sm->pmk_len;
+ }
+
+ if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0,
+ pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0)
+ break;
+
+ if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+ data, data_len) == 0) {
+ if (sm->PMK != pmk) {
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ }
+ ok = 1;
+ break;
+ }
+
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
+ break;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Earlier SNonce did not result in matching MIC");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Earlier SNonce resulted in matching MIC");
+ sm->alt_snonce_valid = 0;
+
+ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
+ goto fail;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) {
+ wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1");
+ wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name,
+ key_len);
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+ os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+ forced_memzero(&PTK, sizeof(PTK));
+ sm->PTK_valid = true;
+
+ ret = 0;
+fail:
+ forced_memzero(pmk_r0, sizeof(pmk_r0));
+ forced_memzero(pmk_r1, sizeof(pmk_r1));
+ return ret;
+}
+
+
+static bool wpa_auth_gtk_rekey_in_process(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group;
+
+ for (group = wpa_auth->group; group; group = group->next) {
+ if (group->GKeyDoneStations)
+ return true;
+ }
+ return false;
+}
+
+
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ u8 *data, size_t data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u16 key_info, key_data_length;
+ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
+ char *msgtxt;
+ struct wpa_eapol_ie_parse kde;
+ const u8 *key_data;
+ size_t keyhdrlen, mic_len;
+ u8 *mic;
+ bool is_mld = false;
+
+ if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
+ return;
+
+#ifdef CONFIG_IEEE80211BE
+ is_mld = sm->mld_assoc_link_id >= 0;
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
+
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
+
+ if (data_len < sizeof(*hdr) + keyhdrlen) {
+ wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
+ return;
+ }
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+ " key_info=0x%x type=%u mic_len=%zu key_data_length=%u",
+ MAC2STR(wpa_auth_get_spa(sm)), key_info, key->type,
+ mic_len, key_data_length);
+ wpa_hexdump(MSG_MSGDUMP,
+ "WPA: EAPOL-Key header (ending before Key MIC)",
+ key, sizeof(*key));
+ wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+ mic, mic_len);
+ if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
+ wpa_printf(MSG_INFO,
+ "WPA: Invalid EAPOL-Key frame - key_data overflow (%d > %zu)",
+ key_data_length,
+ data_len - sizeof(*hdr) - keyhdrlen);
+ return;
+ }
+
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ if (key->type == EAPOL_KEY_TYPE_WPA) {
+ /*
+ * Some deployed station implementations seem to send
+ * msg 4/4 with incorrect type value in WPA2 mode.
+ */
+ wpa_printf(MSG_DEBUG,
+ "Workaround: Allow EAPOL-Key with unexpected WPA type in RSN mode");
+ } else if (key->type != EAPOL_KEY_TYPE_RSN) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore EAPOL-Key with unexpected type %d in RSN mode",
+ key->type);
+ return;
+ }
+ } else {
+ if (key->type != EAPOL_KEY_TYPE_WPA) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore EAPOL-Key with unexpected type %d in WPA mode",
+ key->type);
+ return;
+ }
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
+ WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
+ * are set */
+
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
+ return;
+ }
+
+ /* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from
+ * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the
+ * MLD mechanism here does not work without the somewhat undesired check
+ * on wpa_ptk_state.. Would likely need to decrypt Key Data first to be
+ * able to know which message this is in MLO cases.. */
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ msg = REQUEST;
+ msgtxt = "Request";
+ } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ msg = GROUP_2;
+ msgtxt = "2/2 Group";
+ } else if (key_data_length == 0 ||
+ (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ key_data_length == AES_BLOCK_SIZE) ||
+ (is_mld && (key_info & WPA_KEY_INFO_SECURE) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) {
+ msg = PAIRWISE_4;
+ msgtxt = "4/4 Pairwise";
+ } else {
+ msg = PAIRWISE_2;
+ msgtxt = "2/4 Pairwise";
+ }
+
+ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
+ msg == GROUP_2) {
+ u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (sm->pairwise == WPA_CIPHER_CCMP ||
+ sm->pairwise == WPA_CIPHER_GCMP) {
+ if (wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "advertised support for AES-128-CMAC, but did not use it");
+ return;
+ }
+
+ if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use HMAC-SHA1-AES with CCMP/GCMP");
+ return;
+ }
+ }
+
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+ return;
+ }
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (sm->req_replay_counter_used &&
+ os_memcmp(key->replay_counter, sm->req_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "received EAPOL-Key request with replayed counter");
+ return;
+ }
+ }
+
+ if (!(key_info & WPA_KEY_INFO_REQUEST) &&
+ !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
+ int i;
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
+ {
+ /*
+ * Some supplicant implementations (e.g., Windows XP
+ * WZC) update SNonce for each EAPOL-Key 2/4. This
+ * breaks the workaround on accepting any of the
+ * pending requests, so allow the SNonce to be updated
+ * even if we have already sent out EAPOL-Key 3/4.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
+ sm->update_snonce = 1;
+ os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+ sm->alt_snonce_valid = true;
+ os_memcpy(sm->alt_replay_counter,
+ sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ goto continue_processing;
+ }
+
+ if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(key->replay_counter, sm->alt_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) == 0) {
+ /*
+ * Supplicant may still be using the old SNonce since
+ * there was two EAPOL-Key 2/4 messages and they had
+ * different SNonce values.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
+ goto continue_processing;
+ }
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "ignore retransmitted EAPOL-Key %s - SNonce did not change",
+ msgtxt);
+ } else {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "received EAPOL-Key %s with unexpected replay counter",
+ msgtxt);
+ }
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ wpa_hexdump(MSG_DEBUG, "pending replay counter",
+ sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ wpa_hexdump(MSG_DEBUG, "received replay counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ return;
+ }
+
+continue_processing:
+#ifdef CONFIG_FILS
+ if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
+ !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ switch (msg) {
+ case PAIRWISE_2:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+ sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
+ (!sm->update_snonce ||
+ sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
+ sm->wpa_ptk_state);
+ return;
+ }
+ random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
+ if (sm->group->reject_4way_hs_for_entropy) {
+ /*
+ * The system did not have enough entropy to generate
+ * strong random numbers. Reject the first 4-way
+ * handshake(s) and collect some entropy based on the
+ * information from it. Once enough entropy is
+ * available, the next atempt will trigger GMK/Key
+ * Counter update and the station will be allowed to
+ * continue.
+ */
+ wpa_printf(MSG_DEBUG,
+ "WPA: Reject 4-way handshake to collect more entropy for random number generation");
+ random_mark_pool_ready();
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+ break;
+ case PAIRWISE_4:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
+ !sm->PTK_valid) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
+ sm->wpa_ptk_state);
+ return;
+ }
+ break;
+ case GROUP_2:
+ if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
+ || !sm->PTK_valid) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
+ sm->wpa_ptk_group_state);
+ return;
+ }
+ break;
+ case REQUEST:
+ break;
+ }
+
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "received EAPOL-Key frame (%s)", msgtxt);
+
+ if (key_info & WPA_KEY_INFO_ACK) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "received invalid EAPOL-Key: Key Ack set");
+ return;
+ }
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ !(key_info & WPA_KEY_INFO_MIC)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "received invalid EAPOL-Key: Key MIC not set");
+ return;
+ }
+
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ (key_info & WPA_KEY_INFO_MIC)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "received invalid EAPOL-Key: Key MIC set");
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ sm->MICVerified = false;
+ if (sm->PTK_valid && !sm->update_snonce) {
+ if (mic_len &&
+ wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
+ data, data_len) &&
+ (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+ wpa_try_alt_snonce(sm, data, data_len))) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key with invalid MIC");
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore Key MIC failure for fuzz testing");
+ goto continue_fuzz;
+#endif /* TEST_FUZZ */
+ return;
+ }
+#ifdef CONFIG_FILS
+ if (!mic_len &&
+ wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+ &key_data_length) < 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key with invalid MIC");
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore Key MIC failure for fuzz testing");
+ goto continue_fuzz;
+#endif /* TEST_FUZZ */
+ return;
+ }
+#endif /* CONFIG_FILS */
+#ifdef TEST_FUZZ
+ continue_fuzz:
+#endif /* TEST_FUZZ */
+ sm->MICVerified = true;
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (sm->MICVerified) {
+ sm->req_replay_counter_used = 1;
+ os_memcpy(sm->req_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ } else {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key request with invalid MIC");
+ return;
+ }
+
+ /*
+ * TODO: should decrypt key data field if encryption was used;
+ * even though MAC address KDE is not normally encrypted,
+ * supplicant is allowed to encrypt it.
+ */
+ if (key_info & WPA_KEY_INFO_ERROR) {
+ if (wpa_receive_error_report(
+ wpa_auth, sm,
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
+ return; /* STA entry was removed */
+ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request for new 4-Way Handshake");
+ wpa_request_new_ptk(sm);
+ } else if (key_data_length > 0 &&
+ wpa_parse_kde_ies(key_data, key_data_length,
+ &kde) == 0 &&
+ kde.mac_addr) {
+ } else {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request for GTK rekeying");
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+ if (wpa_auth_gtk_rekey_in_process(wpa_auth))
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
+ "skip new GTK rekey - already in process");
+ else
+ wpa_rekey_gtk(wpa_auth, NULL);
+ }
+ } else {
+ /* Do not allow the same key replay counter to be reused. */
+ wpa_replay_counter_mark_invalid(sm->key_replay,
+ key->replay_counter);
+
+ if (msg == PAIRWISE_2) {
+ /*
+ * Maintain a copy of the pending EAPOL-Key frames in
+ * case the EAPOL-Key frame was retransmitted. This is
+ * needed to allow EAPOL-Key msg 2/4 reply to another
+ * pending msg 1/4 to update the SNonce to work around
+ * unexpected supplicant behavior.
+ */
+ os_memcpy(sm->prev_key_replay, sm->key_replay,
+ sizeof(sm->key_replay));
+ } else {
+ os_memset(sm->prev_key_replay, 0,
+ sizeof(sm->prev_key_replay));
+ }
+
+ /*
+ * Make sure old valid counters are not accepted anymore and
+ * do not get copied again.
+ */
+ wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
+ }
+
+ os_free(sm->last_rx_eapol_key);
+ sm->last_rx_eapol_key = os_memdup(data, data_len);
+ if (!sm->last_rx_eapol_key)
+ return;
+ sm->last_rx_eapol_key_len = data_len;
+
+ sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
+ sm->EAPOLKeyReceived = true;
+ sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+ sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
+ os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+ wpa_sm_step(sm);
+}
+
+
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+ const u8 *gnonce, u8 *gtk, size_t gtk_len)
+{
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
+ u8 *pos;
+ int ret = 0;
+
+ /* GTK = PRF-X(GMK, "Group key expansion",
+ * AA || GNonce || Time || random data)
+ * The example described in the IEEE 802.11 standard uses only AA and
+ * GNonce as inputs here. Add some more entropy since this derivation
+ * is done only at the Authenticator and as such, does not need to be
+ * exactly same.
+ */
+ os_memset(data, 0, sizeof(data));
+ os_memcpy(data, addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+ pos = data + ETH_ALEN + WPA_NONCE_LEN;
+ wpa_get_ntp_timestamp(pos);
+#ifdef TEST_FUZZ
+ os_memset(pos, 0xef, 8);
+#endif /* TEST_FUZZ */
+ pos += 8;
+ if (random_get_bytes(pos, gtk_len) < 0)
+ ret = -1;
+
+#ifdef CONFIG_SHA384
+ if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#else /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA256
+ if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#else /* CONFIG_SHA256 */
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
+
+ forced_memzero(data, sizeof(data));
+
+ return ret;
+}
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct wpa_state_machine *sm = timeout_ctx;
+
+ if (sm->waiting_radius_psk) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "Ignore EAPOL-Key timeout while waiting for RADIUS PSK");
+ return;
+ }
+
+ sm->pending_1_of_4_timeout = 0;
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "EAPOL-Key timeout");
+ sm->TimeoutEvt = true;
+ wpa_sm_step(sm);
+}
+
+
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int key_info,
+ const u8 *key_rsc, const u8 *nonce,
+ const u8 *kde, size_t kde_len,
+ int keyidx, int encr, int force_version)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ size_t len, mic_len, keyhdrlen;
+ int alg;
+ int key_data_len, pad_len = 0;
+ u8 *buf, *pos;
+ int version, pairwise;
+ int i;
+ u8 *key_mic, *key_data;
+
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
+
+ len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
+
+ if (force_version)
+ version = force_version;
+ else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
+ version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+ else if (wpa_use_cmac(sm->wpa_key_mgmt))
+ version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+ else if (sm->pairwise != WPA_CIPHER_TKIP)
+ version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Send EAPOL(version=%d secure=%d mic=%d ack=%d install=%d pairwise=%d kde_len=%zu keyidx=%d encr=%d)",
+ version,
+ (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
+ (key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
+ (key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
+ (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
+ pairwise, kde_len, keyidx, encr);
+
+ key_data_len = kde_len;
+
+ if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
+ version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
+ pad_len = key_data_len % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ key_data_len += pad_len + 8;
+ }
+
+ len += key_data_len;
+ if (!mic_len && encr)
+ len += AES_BLOCK_SIZE;
+
+ hdr = os_zalloc(len);
+ if (!hdr)
+ return;
+ hdr->version = conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = host_to_be16(len - sizeof(*hdr));
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ key_mic = (u8 *) (key + 1);
+ key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
+
+ key->type = sm->wpa == WPA_VERSION_WPA2 ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info |= version;
+ if (encr && sm->wpa == WPA_VERSION_WPA2)
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+ if (sm->wpa != WPA_VERSION_WPA2)
+ key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ WPA_PUT_BE16(key->key_info, key_info);
+
+ alg = pairwise ? sm->pairwise : conf->wpa_group;
+ if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
+ WPA_PUT_BE16(key->key_length, 0);
+ else
+ WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
+
+ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+ sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+ os_memcpy(sm->key_replay[i].counter,
+ sm->key_replay[i - 1].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+ os_memcpy(key->replay_counter, sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ sm->key_replay[0].valid = true;
+
+ if (nonce)
+ os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
+
+ if (key_rsc)
+ os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+
+ if (kde && !encr) {
+ os_memcpy(key_data, kde, kde_len);
+ WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+ } else if (!mic_len && kde) {
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ kde, kde_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+ sm->PTK.kek, sm->PTK.kek_len);
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = (u8 *) hdr;
+ aad_len[0] = key_mic + 2 - (u8 *) hdr;
+ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+ 1, aad, aad_len, key_mic + 2) < 0) {
+ wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+ key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
+ } else if (encr && kde) {
+ buf = os_zalloc(key_data_len);
+ if (!buf) {
+ os_free(hdr);
+ return;
+ }
+ pos = buf;
+ os_memcpy(pos, kde, kde_len);
+ pos += kde_len;
+
+ if (pad_len)
+ *pos++ = 0xdd;
+
+ wpa_hexdump_key(MSG_DEBUG,
+ "Plaintext EAPOL-Key Key Data (+ padding)",
+ buf, key_data_len);
+ if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
+ version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+ sm->PTK.kek, sm->PTK.kek_len);
+ if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+ (key_data_len - 8) / 8, buf, key_data)) {
+ os_free(hdr);
+ bin_clear_free(buf, key_data_len);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Encrypted Key Data from AES-WRAP",
+ key_data, key_data_len);
+ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_FIPS)
+ } else if (sm->PTK.kek_len == 16) {
+ u8 ek[32];
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Encrypt Key Data using RC4");
+ os_memcpy(key->key_iv,
+ sm->group->Counter + WPA_NONCE_LEN - 16, 16);
+ inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+ os_memcpy(key_data, buf, key_data_len);
+ rc4_skip(ek, 32, 256, key_data, key_data_len);
+ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
+#endif /* !(CONFIG_NO_RC4 || CONFIG_FIPS) */
+ } else {
+ os_free(hdr);
+ bin_clear_free(buf, key_data_len);
+ return;
+ }
+ bin_clear_free(buf, key_data_len);
+ }
+
+ if (key_info & WPA_KEY_INFO_MIC) {
+ if (!sm->PTK_valid || !mic_len) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PTK not valid when sending EAPOL-Key frame");
+ os_free(hdr);
+ return;
+ }
+
+ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+ sm->wpa_key_mgmt, version,
+ (u8 *) hdr, len, key_mic) < 0) {
+ os_free(hdr);
+ return;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!pairwise &&
+ conf->corrupt_gtk_rekey_mic_probability > 0.0 &&
+ drand48() < conf->corrupt_gtk_rekey_mic_probability) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "Corrupting group EAPOL-Key Key MIC");
+ key_mic[0]++;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ }
+
+ wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
+ wpa_hexdump(MSG_DEBUG, "Send EAPOL-Key msg", hdr, len);
+ wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
+ sm->pairwise_set);
+ os_free(hdr);
+}
+
+
+static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int key_info,
+ const u8 *key_rsc, const u8 *nonce,
+ const u8 *kde, size_t kde_len,
+ int keyidx, int encr)
+{
+ int timeout_ms;
+ int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+ u32 ctr;
+
+ if (!sm)
+ return;
+
+ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /* When delay_eapol_tx is true, delay the EAPOL-Key transmission by
+ * sending it only on the last attempt after all timeouts for the prior
+ * skipped attemps. */
+ if (wpa_auth->conf.delay_eapol_tx &&
+ ctr != wpa_auth->conf.wpa_pairwise_update_count) {
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+ "DELAY-EAPOL-TX-%d", ctr);
+ goto skip_tx;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
+ keyidx, encr, 0);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_tx:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (ctr == 1 && wpa_auth->conf.tx_status)
+ timeout_ms = pairwise ? eapol_key_timeout_first :
+ eapol_key_timeout_first_group;
+ else
+ timeout_ms = eapol_key_timeout_subseq;
+ if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
+ timeout_ms = eapol_key_timeout_no_retrans;
+ if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+ sm->pending_1_of_4_timeout = 1;
+#ifdef TEST_FUZZ
+ timeout_ms = 1;
+#endif /* TEST_FUZZ */
+ wpa_printf(MSG_DEBUG,
+ "WPA: Use EAPOL-Key timeout of %u ms (retry counter %u)",
+ timeout_ms, ctr);
+ eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, wpa_auth, sm);
+}
+
+
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+ u8 *data, size_t data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u16 key_info;
+ int ret = 0;
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
+ size_t mic_len = wpa_mic_len(akmp, pmk_len);
+
+ if (data_len < sizeof(*hdr) + sizeof(*key))
+ return -1;
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic_pos = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
+ os_memcpy(mic, mic_pos, mic_len);
+ os_memset(mic_pos, 0, mic_len);
+ if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+ key_info & WPA_KEY_INFO_TYPE_MASK,
+ data, data_len, mic_pos) ||
+ os_memcmp_const(mic, mic_pos, mic_len) != 0)
+ ret = -1;
+ os_memcpy(mic_pos, mic, mic_len);
+ return ret;
+}
+
+
+void wpa_remove_ptk(struct wpa_state_machine *sm)
+{
+ sm->PTK_valid = false;
+ os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+ wpa_auth_remove_ptksa(sm->wpa_auth, sm->addr, sm->pairwise);
+
+ if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+ 0, KEY_FLAG_PAIRWISE))
+ wpa_printf(MSG_DEBUG,
+ "RSN: PTK removal from the driver failed");
+ if (sm->use_ext_key_id &&
+ wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 1, NULL,
+ 0, KEY_FLAG_PAIRWISE))
+ wpa_printf(MSG_DEBUG,
+ "RSN: PTK Key ID 1 removal from the driver failed");
+ sm->pairwise_set = false;
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+}
+
+
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
+{
+ int remove_ptk = 1;
+
+ if (!sm)
+ return -1;
+
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "event %d notification", event);
+
+ switch (event) {
+ case WPA_AUTH:
+#ifdef CONFIG_MESH
+ /* PTKs are derived through AMPE */
+ if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+ /* not mesh */
+ break;
+ }
+ return 0;
+#endif /* CONFIG_MESH */
+ case WPA_ASSOC:
+ break;
+ case WPA_DEAUTH:
+ case WPA_DISASSOC:
+ sm->DeauthenticationRequest = true;
+ os_memset(sm->PMK, 0, sizeof(sm->PMK));
+ sm->pmk_len = 0;
+#ifdef CONFIG_IEEE80211R_AP
+ os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+ sm->xxkey_len = 0;
+ os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+ sm->pmk_r1_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
+ break;
+ case WPA_REAUTH:
+ case WPA_REAUTH_EAPOL:
+ if (!sm->started) {
+ /*
+ * When using WPS, we may end up here if the STA
+ * manages to re-associate without the previous STA
+ * entry getting removed. Consequently, we need to make
+ * sure that the WPA state machines gets initialized
+ * properly at this point.
+ */
+ wpa_printf(MSG_DEBUG,
+ "WPA state machine had not been started - initialize now");
+ sm->started = 1;
+ sm->Init = true;
+ if (wpa_sm_step(sm) == 1)
+ return 1; /* should not really happen */
+ sm->Init = false;
+ sm->AuthenticationRequest = true;
+ break;
+ }
+
+ if (sm->ptkstart_without_success > 3) {
+ wpa_printf(MSG_INFO,
+ "WPA: Multiple EAP reauth attempts without 4-way handshake completion, disconnect "
+ MACSTR, MAC2STR(sm->addr));
+ sm->Disconnect = true;
+ break;
+ }
+
+ if (!sm->use_ext_key_id &&
+ sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+ wpa_printf(MSG_INFO,
+ "WPA: PTK0 rekey not allowed, disconnect "
+ MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
+ sm->Disconnect = true;
+ /* Try to encourage the STA to reconnect */
+ sm->disconnect_reason =
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+ break;
+ }
+
+ if (sm->use_ext_key_id)
+ sm->keyidx_active ^= 1; /* flip Key ID */
+
+ if (sm->GUpdateStationKeys) {
+ /*
+ * Reauthentication cancels the pending group key
+ * update for this STA.
+ */
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+ sm->PtkGroupInit = true;
+ }
+ sm->ReAuthenticationRequest = true;
+ break;
+ case WPA_ASSOC_FT:
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_printf(MSG_DEBUG,
+ "FT: Retry PTK configuration after association");
+ wpa_ft_install_ptk(sm, 1);
+
+ /* Using FT protocol, not WPA auth state machine */
+ sm->ft_completed = 1;
+ wpa_auth_set_ptk_rekey_timer(sm);
+ return 0;
+#else /* CONFIG_IEEE80211R_AP */
+ break;
+#endif /* CONFIG_IEEE80211R_AP */
+ case WPA_ASSOC_FILS:
+#ifdef CONFIG_FILS
+ wpa_printf(MSG_DEBUG,
+ "FILS: TK configuration after association");
+ fils_set_tk(sm);
+ sm->fils_completed = 1;
+ return 0;
+#else /* CONFIG_FILS */
+ break;
+#endif /* CONFIG_FILS */
+ case WPA_DRV_STA_REMOVED:
+ sm->tk_already_set = false;
+ return 0;
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if (sm->mgmt_frame_prot && event == WPA_AUTH)
+ remove_ptk = 0;
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ (event == WPA_AUTH || event == WPA_ASSOC))
+ remove_ptk = 0;
+#endif /* CONFIG_FILS */
+
+ if (remove_ptk) {
+ sm->PTK_valid = false;
+ os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+ if (event != WPA_REAUTH_EAPOL)
+ wpa_remove_ptk(sm);
+ }
+
+ if (sm->in_step_loop) {
+ /*
+ * wpa_sm_step() is already running - avoid recursive call to
+ * it by making the existing loop process the new update.
+ */
+ sm->changed = true;
+ return 0;
+ }
+ return wpa_sm_step(sm);
+}
+
+
+SM_STATE(WPA_PTK, INITIALIZE)
+{
+ SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
+ if (sm->Init) {
+ /* Init flag is not cleared here, so avoid busy
+ * loop by claiming nothing changed. */
+ sm->changed = false;
+ }
+
+ sm->keycount = 0;
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = false;
+ if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
+ * Local AA > Remote AA)) */) {
+ sm->Pair = true;
+ }
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
+ wpa_remove_ptk(sm);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
+ sm->TimeoutCtr = 0;
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_authorized, 0);
+ }
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECT)
+{
+ u16 reason = sm->disconnect_reason;
+
+ SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
+ sm->Disconnect = false;
+ sm->disconnect_reason = 0;
+ if (!reason)
+ reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECTED)
+{
+ SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
+ sm->DeauthenticationRequest = false;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION)
+{
+ SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
+ os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+ sm->PTK_valid = false;
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
+ 1);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
+ sm->AuthenticationRequest = false;
+}
+
+
+static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ if (group->first_sta_seen)
+ return;
+ /*
+ * System has run bit further than at the time hostapd was started
+ * potentially very early during boot up. This provides better chances
+ * of collecting more randomness on embedded systems. Re-initialize the
+ * GMK and Counter here to improve their strength if there was not
+ * enough entropy available immediately after system startup.
+ */
+ wpa_printf(MSG_DEBUG,
+ "WPA: Re-initialize GMK/Counter on first station");
+ if (random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO,
+ "WPA: Not enough entropy in random pool to proceed - reject first 4-way handshake");
+ group->reject_4way_hs_for_entropy = true;
+ } else {
+ group->first_sta_seen = true;
+ group->reject_4way_hs_for_entropy = false;
+ }
+
+ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+ wpa_gtk_update(wpa_auth, group) < 0 ||
+ wpa_group_config_group_keys(wpa_auth, group) < 0) {
+ wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+ group->first_sta_seen = false;
+ group->reject_4way_hs_for_entropy = true;
+ }
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION2)
+{
+ SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+ wpa_group_ensure_init(sm->wpa_auth, sm->group);
+ sm->ReAuthenticationRequest = false;
+
+ /*
+ * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+ * ambiguous. The Authenticator state machine uses a counter that is
+ * incremented by one for each 4-way handshake. However, the security
+ * analysis of 4-way handshake points out that unpredictable nonces
+ * help in preventing precomputation attacks. Instead of the state
+ * machine definition, use an unpredictable nonce value here to provide
+ * stronger protection against potential precomputation attacks.
+ */
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to get random data for ANonce.");
+ sm->Disconnect = true;
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+ WPA_NONCE_LEN);
+ /* IEEE 802.11i does not clear TimeoutCtr here, but this is more
+ * logical place than INITIALIZE since AUTHENTICATION2 can be
+ * re-entered on ReAuthenticationRequest without going through
+ * INITIALIZE. */
+ sm->TimeoutCtr = 0;
+}
+
+
+static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm)
+{
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to get random data for ANonce");
+ sm->Disconnect = true;
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce,
+ WPA_NONCE_LEN);
+ sm->TimeoutCtr = 0;
+ return 0;
+}
+
+
+SM_STATE(WPA_PTK, INITPMK)
+{
+ u8 msk[2 * PMK_LEN];
+ size_t len = 2 * PMK_LEN;
+
+ SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
+#ifdef CONFIG_IEEE80211R_AP
+ sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
+ if (sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+ sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_DPP
+ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PMKSA cache entry for STA - reject connection");
+ sm->Disconnect = true;
+ sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
+ return;
+#endif /* CONFIG_DPP */
+ } else if (wpa_auth_get_msk(sm->wpa_auth, wpa_auth_get_spa(sm),
+ msk, &len) == 0) {
+ unsigned int pmk_len;
+
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ pmk_len = PMK_LEN_SUITE_B_192;
+ else
+ pmk_len = PMK_LEN;
+ wpa_printf(MSG_DEBUG,
+ "WPA: PMK from EAPOL state machine (MSK len=%zu PMK len=%u)",
+ len, pmk_len);
+ if (len < pmk_len) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: MSK not long enough (%zu) to create PMK (%u)",
+ len, pmk_len);
+ sm->Disconnect = true;
+ return;
+ }
+ os_memcpy(sm->PMK, msk, pmk_len);
+ sm->pmk_len = pmk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (len >= 2 * PMK_LEN) {
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
+ sm->xxkey_len = SHA384_MAC_LEN;
+ } else {
+ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+ sm->wpa_auth->cb->get_msk);
+ sm->Disconnect = true;
+ return;
+ }
+ forced_memzero(msk, sizeof(msk));
+
+ sm->req_replay_counter_used = 0;
+ /* IEEE 802.11i does not set keyRun to false, but not doing this
+ * will break reauthentication since EAPOL state machines may not be
+ * get into AUTHENTICATING state that clears keyRun before WPA state
+ * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
+ * state and takes PMK from the previously used AAA Key. This will
+ * eventually fail in 4-Way Handshake because Supplicant uses PMK
+ * derived from the new AAA Key. Setting keyRun = false here seems to
+ * be good workaround for this issue. */
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, false);
+}
+
+
+SM_STATE(WPA_PTK, INITPSK)
+{
+ const u8 *psk;
+ size_t psk_len;
+
+ SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
+ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
+ &psk_len, NULL);
+ if (psk) {
+ os_memcpy(sm->PMK, psk, psk_len);
+ sm->pmk_len = psk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ sm->xxkey_len = PMK_LEN;
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (psk_len == SHA512_MAC_LEN || psk_len == SHA384_MAC_LEN ||
+ psk_len == SHA256_MAC_LEN))
+ sm->xxkey_len = psk_len;
+#endif /* CONFIG_SAE */
+ os_memcpy(sm->xxkey, psk, sm->xxkey_len);
+#endif /* CONFIG_IEEE80211R_AP */
+ }
+#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache (len=%zu)",
+ sm->pmksa->pmk_len);
+ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+ sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len);
+ sm->xxkey_len = sm->pmksa->pmk_len;
+#endif /* CONFIG_IEEE80211R_AP */
+ }
+#endif /* CONFIG_SAE */
+ sm->req_replay_counter_used = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKSTART)
+{
+ u8 buf[2 * (2 + RSN_SELECTOR_LEN) + PMKID_LEN + ETH_ALEN];
+ u8 *pmkid = NULL;
+ size_t kde_len = 0;
+ u16 key_info;
+
+ SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
+ sm->PTKRequest = false;
+ sm->TimeoutEvt = false;
+ sm->alt_snonce_valid = false;
+ sm->ptkstart_without_success++;
+
+ sm->TimeoutCtr++;
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake");
+ /*
+ * For infrastructure BSS cases, it is better for the AP not to include
+ * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate
+ * offline search for the passphrase/PSK without having to be able to
+ * capture a 4-way handshake from a STA that has access to the network.
+ *
+ * For IBSS cases, addition of PMKID KDE could be considered even with
+ * WPA2-PSK cases that use multiple PSKs, but only if there is a single
+ * possible PSK for this STA. However, this should not be done unless
+ * there is support for using that information on the supplicant side.
+ * The concern about exposing PMKID unnecessarily in infrastructure BSS
+ * cases would also apply here, but at least in the IBSS case, this
+ * would cover a potential real use case.
+ */
+ if (sm->wpa == WPA_VERSION_WPA2 &&
+ (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
+ (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+ pmkid = buf;
+ kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+ pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
+ pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+ RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
+ if (sm->pmksa) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from PMKSA entry",
+ sm->pmksa->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmksa->pmkid, PMKID_LEN);
+ } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+ /* No KCK available to derive PMKID */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No KCK available to derive PMKID for message 1/4");
+ pmkid = NULL;
+#ifdef CONFIG_FILS
+ } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ if (sm->pmkid_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from FILS/ERP",
+ sm->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmkid, PMKID_LEN);
+ } else {
+ /* No PMKID available */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No FILS/ERP PMKID available for message 1/4");
+ pmkid = NULL;
+ }
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+ } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+ sm->ft_completed) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMKID in message 1/4 when using FT protocol");
+ pmkid = NULL;
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+ } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ if (sm->pmkid_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from SAE",
+ sm->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmkid, PMKID_LEN);
+ } else {
+ /* No PMKID available */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No SAE PMKID available for message 1/4");
+ pmkid = NULL;
+ }
+#endif /* CONFIG_SAE */
+ } else {
+ /*
+ * Calculate PMKID since no PMKSA cache entry was
+ * available with pre-calculated PMKID.
+ */
+ rsn_pmkid(sm->PMK, sm->pmk_len,
+ wpa_auth_get_aa(sm),
+ wpa_auth_get_spa(sm),
+ &pmkid[2 + RSN_SELECTOR_LEN],
+ sm->wpa_key_mgmt);
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID derived from PMK",
+ &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
+ }
+ }
+ if (!pmkid)
+ kde_len = 0;
+
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Add MAC Address KDE: kde_len=%zu",
+ kde_len);
+ wpa_add_kde(buf + kde_len, RSN_KEY_DATA_MAC_ADDR,
+ sm->own_mld_addr, ETH_ALEN, NULL, 0);
+ kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ key_info = WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE;
+ if (sm->pairwise_set && sm->wpa != WPA_VERSION_WPA)
+ key_info |= WPA_KEY_INFO_SECURE;
+ wpa_send_eapol(sm->wpa_auth, sm, key_info, NULL,
+ sm->ANonce, kde_len ? buf : NULL, kde_len, 0, 0);
+}
+
+
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+ const u8 *pmk, unsigned int pmk_len,
+ struct wpa_ptk *ptk, int force_sha256,
+ u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+ size_t *key_len)
+{
+ const u8 *z = NULL;
+ size_t z_len = 0, kdk_len;
+ int akmp;
+ int ret;
+
+ if (sm->wpa_auth->conf.force_kdk_derivation ||
+ (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+ kdk_len = WPA_KDK_MAX_LEN;
+ else
+ kdk_len = 0;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ if (sm->ft_completed) {
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+
+ ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+ sm->SNonce, sm->ANonce,
+ wpa_auth_get_spa(sm),
+ wpa_auth_get_aa(sm),
+ sm->pmk_r1_name, ptk,
+ ptk_name, sm->wpa_key_mgmt,
+ sm->pairwise, kdk_len);
+ } else {
+ ret = wpa_auth_derive_ptk_ft(sm, ptk, pmk_r0, pmk_r1,
+ pmk_r0_name, key_len,
+ kdk_len);
+ }
+ if (ret) {
+ wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+ return ret;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF)) {
+ ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
+ sm->pairwise);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "FT: LTF keyseed derivation failed");
+ }
+ }
+#endif /* CONFIG_PASN */
+ return ret;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_DPP2
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+ z = wpabuf_head(sm->dpp_z);
+ z_len = wpabuf_len(sm->dpp_z);
+ }
+#endif /* CONFIG_DPP2 */
+
+ akmp = sm->wpa_key_mgmt;
+ if (force_sha256)
+ akmp |= WPA_KEY_MGMT_PSK_SHA256;
+ ret = wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+ wpa_auth_get_aa(sm), wpa_auth_get_spa(sm),
+ sm->ANonce, snonce, ptk, akmp,
+ sm->pairwise, z, z_len, kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: PTK derivation failed");
+ return ret;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+ ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: LTF keyseed derivation failed");
+ }
+ }
+#endif /* CONFIG_PASN */
+ return ret;
+}
+
+
+#ifdef CONFIG_FILS
+
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *snonce, const u8 *anonce,
+ const u8 *dhss, size_t dhss_len,
+ struct wpabuf *g_sta, struct wpabuf *g_ap)
+{
+ u8 ick[FILS_ICK_MAX_LEN];
+ size_t ick_len;
+ int res;
+ u8 fils_ft[FILS_FT_MAX_LEN];
+ size_t fils_ft_len = 0, kdk_len;
+
+ if (sm->wpa_auth->conf.force_kdk_derivation ||
+ (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+ kdk_len = WPA_KDK_MAX_LEN;
+ else
+ kdk_len = 0;
+
+ res = fils_pmk_to_ptk(pmk, pmk_len, wpa_auth_get_spa(sm),
+ wpa_auth_get_aa(sm),
+ snonce, anonce, dhss, dhss_len,
+ &sm->PTK, ick, &ick_len,
+ sm->wpa_key_mgmt, sm->pairwise,
+ fils_ft, &fils_ft_len, kdk_len);
+ if (res < 0)
+ return res;
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+ res = wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, sm->pairwise);
+ if (res) {
+ wpa_printf(MSG_ERROR,
+ "FILS: LTF keyseed derivation failed");
+ return res;
+ }
+ }
+#endif /* CONFIG_PASN */
+
+ sm->PTK_valid = true;
+ sm->tk_already_set = false;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (fils_ft_len) {
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+
+ if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
+ conf->ssid, conf->ssid_len,
+ conf->mobility_domain,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ wpa_auth_get_spa(sm), pmk_r0, pmk_r0_name,
+ sm->wpa_key_mgmt) < 0)
+ return -1;
+
+ wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
+ forced_memzero(fils_ft, sizeof(fils_ft));
+
+ res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
+ wpa_auth_get_spa(sm),
+ sm->pmk_r1_name,
+ fils_ft_len);
+ forced_memzero(pmk_r0, PMK_LEN_MAX);
+ if (res < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ sm->pmk_r1_name_valid = 1;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+ wpa_auth_get_spa(sm),
+ wpa_auth_get_aa(sm),
+ g_sta ? wpabuf_head(g_sta) : NULL,
+ g_sta ? wpabuf_len(g_sta) : 0,
+ g_ap ? wpabuf_head(g_ap) : NULL,
+ g_ap ? wpabuf_len(g_ap) : 0,
+ sm->wpa_key_mgmt, sm->fils_key_auth_sta,
+ sm->fils_key_auth_ap,
+ &sm->fils_key_auth_len);
+ forced_memzero(ick, sizeof(ick));
+
+ /* Store nonces for (Re)Association Request/Response frame processing */
+ os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+ os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+ return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u8 *pos;
+ u16 key_data_len;
+ u8 *tmp;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ pos = (u8 *) (key + 1);
+ key_data_len = WPA_GET_BE16(pos);
+ if (key_data_len < AES_BLOCK_SIZE ||
+ key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "No room for AES-SIV data in the frame");
+ return -1;
+ }
+ pos += 2; /* Pointing at the Encrypted Key Data field */
+
+ tmp = os_malloc(key_data_len);
+ if (!tmp)
+ return -1;
+
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = buf;
+ aad_len[0] = pos - buf;
+ if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
+ 1, aad, aad_len, tmp) < 0) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "Invalid AES-SIV data in the frame");
+ bin_clear_free(tmp, key_data_len);
+ return -1;
+ }
+
+ /* AEAD decryption and validation completed successfully */
+ key_data_len -= AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+ tmp, key_data_len);
+
+ /* Replace Key Data field with the decrypted version */
+ os_memcpy(pos, tmp, key_data_len);
+ pos -= 2; /* Key Data Length field */
+ WPA_PUT_BE16(pos, key_data_len);
+ bin_clear_free(tmp, key_data_len);
+ if (_key_data_len)
+ *_key_data_len = key_data_len;
+ return 0;
+}
+
+
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *fils_session)
+{
+ const u8 *ie, *end;
+ const u8 *session = NULL;
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not a FILS AKM - reject association");
+ return NULL;
+ }
+
+ /* Verify Session element */
+ ie = ies;
+ end = ((const u8 *) ie) + ies_len;
+ while (ie + 1 < end) {
+ if (ie + 2 + ie[1] > end)
+ break;
+ if (ie[0] == WLAN_EID_EXTENSION &&
+ ie[1] >= 1 + FILS_SESSION_LEN &&
+ ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+ session = ie;
+ break;
+ }
+ ie += 2 + ie[1];
+ }
+
+ if (!session) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
+ __func__);
+ return NULL;
+ }
+
+ if (!fils_session) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: %s: Could not find FILS Session element in STA entry - reject",
+ __func__);
+ return NULL;
+ }
+
+ if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ fils_session, FILS_SESSION_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+ session + 3, FILS_SESSION_LEN);
+ return NULL;
+ }
+ return session;
+}
+
+
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to parse decrypted elements");
+ return -1;
+ }
+
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ return -1;
+ }
+
+ if (!elems.fils_key_confirm) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+ return -1;
+ }
+
+ if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unexpected Key-Auth length %d (expected %zu)",
+ elems.fils_key_confirm_len,
+ sm->fils_key_auth_len);
+ return -1;
+ }
+
+ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+ sm->fils_key_auth_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+ elems.fils_key_confirm, elems.fils_key_confirm_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+ sm->fils_key_auth_sta, sm->fils_key_auth_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+ const struct ieee80211_mgmt *mgmt, size_t frame_len,
+ u8 *pos, size_t left)
+{
+ u16 fc, stype;
+ const u8 *end, *ie_start, *ie, *session, *crypt;
+ const u8 *aad[5];
+ size_t aad_len[5];
+
+ if (!sm || !sm->PTK_valid) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No KEK to decrypt Assocication Request frame");
+ return -1;
+ }
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not a FILS AKM - reject association");
+ return -1;
+ }
+
+ end = ((const u8 *) mgmt) + frame_len;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+ ie_start = mgmt->u.reassoc_req.variable;
+ else
+ ie_start = mgmt->u.assoc_req.variable;
+ ie = ie_start;
+
+ /*
+ * Find FILS Session element which is the last unencrypted element in
+ * the frame.
+ */
+ session = wpa_fils_validate_fils_session(sm, ie, end - ie,
+ fils_session);
+ if (!session) {
+ wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
+ return -1;
+ }
+
+ crypt = session + 2 + session[1];
+
+ if (end - crypt < AES_BLOCK_SIZE) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Too short frame to include AES-SIV data");
+ return -1;
+ }
+
+ /* AES-SIV AAD vectors */
+
+ /* The STA's MAC address */
+ aad[0] = mgmt->sa;
+ aad_len[0] = ETH_ALEN;
+ /* The AP's BSSID */
+ aad[1] = mgmt->da;
+ aad_len[1] = ETH_ALEN;
+ /* The STA's nonce */
+ aad[2] = sm->SNonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The AP's nonce */
+ aad[3] = sm->ANonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Request frame from the Capability Information
+ * field to the FILS Session element (both inclusive).
+ */
+ aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+ aad_len[4] = crypt - aad[4];
+
+ if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+ 5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Invalid AES-SIV data in the frame");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+ pos, left - AES_BLOCK_SIZE);
+
+ if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
+ return -1;
+ }
+
+ return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+ size_t current_len, size_t max_len,
+ const struct wpabuf *hlp)
+{
+ u8 *end = buf + max_len;
+ u8 *pos = buf + current_len;
+ struct ieee80211_mgmt *mgmt;
+ struct wpabuf *plain;
+ const u8 *aad[5];
+ size_t aad_len[5];
+
+ if (!sm || !sm->PTK_valid)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: Association Response frame before FILS processing",
+ buf, current_len);
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+
+ /* AES-SIV AAD vectors */
+
+ /* The AP's BSSID */
+ aad[0] = mgmt->sa;
+ aad_len[0] = ETH_ALEN;
+ /* The STA's MAC address */
+ aad[1] = mgmt->da;
+ aad_len[1] = ETH_ALEN;
+ /* The AP's nonce */
+ aad[2] = sm->ANonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The STA's nonce */
+ aad[3] = sm->SNonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Response frame from the Capability Information
+ * field (the same offset in both Association and Reassociation
+ * Response frames) to the FILS Session element (both inclusive).
+ */
+ aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+ aad_len[4] = pos - aad[4];
+
+ /* The following elements will be encrypted with AES-SIV */
+ plain = fils_prepare_plainbuf(sm, hlp);
+ if (!plain) {
+ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+ return -1;
+ }
+
+ if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not enough room for FILS elements");
+ wpabuf_clear_free(plain);
+ return -1;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+ plain);
+
+ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+ wpabuf_head(plain), wpabuf_len(plain),
+ 5, aad, aad_len, pos) < 0) {
+ wpabuf_clear_free(plain);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: Encrypted Association Response elements",
+ pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+ current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+ wpabuf_clear_free(plain);
+
+ sm->fils_completed = 1;
+
+ return current_len;
+}
+
+
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+ const struct wpabuf *hlp)
+{
+ struct wpabuf *plain;
+ u8 *len, *tmp, *tmp2;
+ u8 hdr[2];
+ u8 *gtk, stub_gtk[32];
+ size_t gtk_len;
+ struct wpa_group *gsm;
+ size_t plain_len;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+
+ plain_len = 1000 + ieee80211w_kde_len(sm);
+ if (conf->transition_disable)
+ plain_len += 2 + RSN_SELECTOR_LEN + 1;
+ plain = wpabuf_alloc(plain_len);
+ if (!plain)
+ return NULL;
+
+ /* TODO: FILS Public Key */
+
+ /* FILS Key Confirmation */
+ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+ /* FILS HLP Container */
+ if (hlp)
+ wpabuf_put_buf(plain, hlp);
+
+ /* TODO: FILS IP Address Assignment */
+
+ /* Key Delivery */
+ gsm = sm->group;
+ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+ len = wpabuf_put(plain, 1);
+ wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+ wpabuf_put(plain, WPA_KEY_RSC_LEN));
+ /* GTK KDE */
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(stub_gtk, gtk_len) < 0) {
+ wpabuf_clear_free(plain);
+ return NULL;
+ }
+ gtk = stub_gtk;
+ }
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ wpabuf_put(plain, tmp2 - tmp);
+
+ /* IGTK KDE and BIGTK KDE */
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = ieee80211w_kde_add(sm, tmp);
+ wpabuf_put(plain, tmp2 - tmp);
+
+ if (conf->transition_disable) {
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = wpa_add_kde(tmp, WFA_KEY_DATA_TRANSITION_DISABLE,
+ &conf->transition_disable, 1, NULL, 0);
+ wpabuf_put(plain, tmp2 - tmp);
+ }
+
+ *len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ u8 *pos;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "FILS: Failed to get channel info for OCI element");
+ wpabuf_clear_free(plain);
+ return NULL;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->oci_freq_override_fils_assoc) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ conf->oci_freq_override_fils_assoc);
+ ci.frequency = conf->oci_freq_override_fils_assoc;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
+ if (ocv_insert_extended_oci(&ci, pos) < 0) {
+ wpabuf_clear_free(plain);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ return plain;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+ enum wpa_alg alg;
+ int klen;
+
+ if (!sm || !sm->PTK_valid) {
+ wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
+ return -1;
+ }
+ if (sm->tk_already_set) {
+ wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
+ return -1;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise);
+ klen = wpa_cipher_key_len(sm->pairwise);
+
+ wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+ sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX)) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+ return -1;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to set LTF keyseed to driver");
+ return -1;
+ }
+#endif /* CONFIG_PASN */
+
+ sm->pairwise_set = true;
+ sm->tk_already_set = true;
+
+ wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+ dot11RSNAConfigPMKLifetime, &sm->PTK);
+
+ return 0;
+}
+
+
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
+ const u8 *fils_session, struct wpabuf *hlp)
+{
+ struct wpabuf *plain;
+ u8 *pos = buf;
+
+ /* FILS Session */
+ *pos++ = WLAN_EID_EXTENSION; /* Element ID */
+ *pos++ = 1 + FILS_SESSION_LEN; /* Length */
+ *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+ os_memcpy(pos, fils_session, FILS_SESSION_LEN);
+ pos += FILS_SESSION_LEN;
+
+ plain = fils_prepare_plainbuf(sm, hlp);
+ if (!plain) {
+ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+ return NULL;
+ }
+
+ os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
+ pos += wpabuf_len(plain);
+
+ wpa_printf(MSG_DEBUG, "%s: plain buf_len: %zu", __func__,
+ wpabuf_len(plain));
+ wpabuf_clear_free(plain);
+ sm->fils_completed = 1;
+ return pos;
+}
+
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_OCV
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+ if (!wpa_auth->cb->get_sta_tx_params)
+ return -1;
+ return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr,
+ ap_max_chanwidth, ap_seg1_idx,
+ bandwidth, seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
+static int wpa_auth_validate_ml_kdes_m2(struct wpa_state_machine *sm,
+ struct wpa_eapol_ie_parse *kde)
+{
+#ifdef CONFIG_IEEE80211BE
+ int i;
+ unsigned int n_links = 0;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ /* MLD MAC address must be the same */
+ if (!kde->mac_addr ||
+ os_memcmp(kde->mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid MLD address");
+ return -1;
+ }
+
+ /* Find matching link ID and the MAC address for each link */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(kde->valid_mlo_links & BIT(i)))
+ continue;
+
+ /*
+ * Each entry should contain the link information and the MAC
+ * address.
+ */
+ if (kde->mlo_link_len[i] != 1 + ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Invalid MLO Link (ID %u) KDE len=%zu",
+ i, kde->mlo_link_len[i]);
+ return -1;
+ }
+
+ if (!sm->mld_links[i].valid || i == sm->mld_assoc_link_id) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Invalid link ID=%u", i);
+ return -1;
+ }
+
+ if (os_memcmp(sm->mld_links[i].peer_addr, kde->mlo_link[i] + 1,
+ ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: invalid MAC address=" MACSTR
+ " expected " MACSTR " (link ID %u)",
+ MAC2STR(kde->mlo_link[i] + 1),
+ MAC2STR(sm->mld_links[i].peer_addr), i);
+ return -1;
+ }
+
+ n_links++;
+ }
+
+ /* Must have the same number of MLO links (excluding the local one) */
+ if (n_links != sm->n_mld_affiliated_links) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLD: Expecting %u MLD links in msg 2, but got %u",
+ sm->n_mld_affiliated_links, n_links);
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_ptk PTK;
+ int ok = 0, psk_found = 0;
+ const u8 *pmk = NULL;
+ size_t pmk_len;
+ int ft;
+ const u8 *eapol_key_ie, *key_data, *mic;
+ u16 key_data_length;
+ size_t mic_len, eapol_key_ie_len;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
+ int vlan_id = 0;
+ int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround;
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t key_len;
+
+ SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+ sm->EAPOLKeyReceived = false;
+ sm->update_snonce = false;
+ os_memset(&PTK, 0, sizeof(PTK));
+
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+ /* WPA with IEEE 802.1X: use the derived PMK from EAP
+ * WPA-PSK: iterate through possible PSKs and select the one matching
+ * the packet */
+ for (;;) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+ sm->p2p_dev_addr, pmk, &pmk_len,
+ &vlan_id);
+ if (!pmk)
+ break;
+ psk_found = 1;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ } else {
+ pmk = sm->PMK;
+ pmk_len = sm->pmk_len;
+ }
+
+ if ((!pmk || !pmk_len) && sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
+ pmk = sm->pmksa->pmk;
+ pmk_len = sm->pmksa->pmk_len;
+ }
+
+ if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
+ owe_ptk_workaround == 2, pmk_r0, pmk_r1,
+ pmk_r0_name, &key_len) < 0)
+ break;
+
+ if (mic_len &&
+ wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+ sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len) == 0) {
+ if (sm->PMK != pmk) {
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ }
+ ok = 1;
+ break;
+ }
+
+#ifdef CONFIG_FILS
+ if (!mic_len &&
+ wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len, NULL) == 0) {
+ ok = 1;
+ break;
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && pmk_len > 32 &&
+ owe_ptk_workaround == 1) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Try PTK derivation workaround with SHA256");
+ owe_ptk_workaround = 2;
+ continue;
+ }
+#endif /* CONFIG_OWE */
+
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
+ break;
+ }
+
+ if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+ wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk &&
+ !sm->waiting_radius_psk) {
+ wpa_printf(MSG_DEBUG, "No PSK available - ask RADIUS server");
+ wpa_auth->cb->request_radius_psk(wpa_auth->cb_ctx, sm->addr,
+ sm->wpa_key_mgmt,
+ sm->ANonce,
+ sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len);
+ sm->waiting_radius_psk = 1;
+ goto out;
+ }
+
+ if (!ok) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "invalid MIC in msg 2/4 of 4-Way Handshake");
+ if (psk_found)
+ wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
+ goto out;
+ }
+
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+ sizeof(*key) - mic_len - 2)
+ goto out;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "received EAPOL-Key msg 2/4 with invalid Key Data contents");
+ goto out;
+ }
+ if (kde.rsn_ie) {
+ eapol_key_ie = kde.rsn_ie;
+ eapol_key_ie_len = kde.rsn_ie_len;
+ } else if (kde.osen) {
+ eapol_key_ie = kde.osen;
+ eapol_key_ie_len = kde.osen_len;
+ } else {
+ eapol_key_ie = kde.wpa_ie;
+ eapol_key_ie_len = kde.wpa_ie_len;
+ }
+ ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+ if (!sm->wpa_ie ||
+ wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
+ eapol_key_ie, eapol_key_ie_len)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "WPA IE from (Re)AssocReq did not match with msg 2/4");
+ if (sm->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+ sm->wpa_ie, sm->wpa_ie_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+ eapol_key_ie, eapol_key_ie_len);
+ /* MLME-DEAUTHENTICATE.request */
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ goto out;
+ }
+ if ((!sm->rsnxe && kde.rsnxe) ||
+ (sm->rsnxe && !kde.rsnxe) ||
+ (sm->rsnxe && kde.rsnxe &&
+ (sm->rsnxe_len != kde.rsnxe_len ||
+ os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
+ wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
+ sm->rsnxe, sm->rsnxe_len);
+ wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
+ kde.rsnxe, kde.rsnxe_len);
+ /* MLME-DEAUTHENTICATE.request */
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ goto out;
+ }
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+ enum oci_verify_result res;
+
+ if (wpa_channel_info(wpa_auth, &ci) != 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
+ goto out;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ goto out;
+
+ res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx);
+ if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+ /* Work around misbehaving STAs */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "Disable OCV with a STA that does not send OCI");
+ wpa_auth_set_ocv(sm, 0);
+ } else if (res != OCI_SUCCESS) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "OCV failed: %s", ocv_errorstr);
+ if (wpa_auth->conf.msg_ctx)
+ wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
+ OCV_FAILURE "addr=" MACSTR
+ " frame=eapol-key-m2 error=%s",
+ MAC2STR(wpa_auth_get_spa(sm)),
+ ocv_errorstr);
+ goto out;
+ }
+ }
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211R_AP
+ if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ goto out;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+ if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+ int idx;
+ wpa_printf(MSG_DEBUG,
+ "P2P: IP address requested in EAPOL-Key exchange");
+ idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+ if (idx >= 0) {
+ u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+ bitfield_set(wpa_auth->ip_pool, idx);
+ sm->ip_addr_bit = idx;
+ WPA_PUT_BE32(sm->ip_addr, start + idx);
+ wpa_printf(MSG_DEBUG,
+ "P2P: Assigned IP address %u.%u.%u.%u to "
+ MACSTR " (bit %u)",
+ sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(wpa_auth_get_spa(sm)),
+ sm->ip_addr_bit);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && kde.dpp_kde) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: peer Protocol Version %u Flags 0x%x",
+ kde.dpp_kde[0], kde.dpp_kde[1]);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+ wpa_auth->conf.dpp_pfs != 2 &&
+ (kde.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) &&
+ !sm->dpp_z) {
+ wpa_printf(MSG_INFO,
+ "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ goto out;
+ }
+ }
+#endif /* CONFIG_DPP2 */
+
+ if (wpa_auth_validate_ml_kdes_m2(sm, &kde) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ /*
+ * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+ * with the value we derived.
+ */
+ if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PMKR1Name mismatch in FT 4-way handshake");
+ wpa_hexdump(MSG_DEBUG,
+ "FT: PMKR1Name from Supplicant",
+ sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ goto out;
+ }
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ goto out;
+ }
+
+ sm->pending_1_of_4_timeout = 0;
+ eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && sm->PMK != pmk) {
+ /* PSK may have changed from the previous choice, so update
+ * state machine data based on whatever PSK was selected here.
+ */
+ os_memcpy(sm->PMK, pmk, PMK_LEN);
+ sm->pmk_len = PMK_LEN;
+ }
+
+ sm->MICVerified = true;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) {
+ wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1");
+ wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name,
+ key_len);
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+ forced_memzero(&PTK, sizeof(PTK));
+ sm->PTK_valid = true;
+out:
+ forced_memzero(pmk_r0, sizeof(pmk_r0));
+ forced_memzero(pmk_r1, sizeof(pmk_r1));
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
+{
+ SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
+ sm->TimeoutCtr = 0;
+}
+
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+ size_t len = 0;
+
+ if (sm->mgmt_frame_prot) {
+ len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
+ len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ }
+ if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
+ len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
+ len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ }
+
+ return len;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_igtk_kde igtk;
+ struct wpa_bigtk_kde bigtk;
+ struct wpa_group *gsm = sm->group;
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ size_t len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+
+ if (!sm->mgmt_frame_prot)
+ return pos;
+
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ return pos; /* Use per-link MLO KDEs instead */
+#endif /* CONFIG_IEEE80211BE */
+
+ igtk.keyid[0] = gsm->GN_igtk;
+ igtk.keyid[1] = 0;
+ if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
+ os_memset(igtk.pn, 0, sizeof(igtk.pn));
+ else
+ os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+ os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use of
+ * IGTK in the BSS.
+ */
+ if (random_get_bytes(igtk.igtk, len) < 0)
+ return pos;
+ }
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
+ (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+ NULL, 0);
+ forced_memzero(&igtk, sizeof(igtk));
+
+ if (!conf->beacon_prot)
+ return pos;
+
+ bigtk.keyid[0] = gsm->GN_bigtk;
+ bigtk.keyid[1] = 0;
+ if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
+ os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
+ else
+ os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
+ os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random BIGTK to each OSEN STA to prevent use
+ * of BIGTK in the BSS.
+ */
+ if (random_get_bytes(bigtk.bigtk, len) < 0)
+ return pos;
+ }
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
+ (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
+ NULL, 0);
+ forced_memzero(&bigtk, sizeof(bigtk));
+
+ return pos;
+}
+
+
+static int ocv_oci_len(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm))
+ return OCV_OCI_KDE_LEN;
+#endif /* CONFIG_OCV */
+ return 0;
+}
+
+
+static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos,
+ unsigned int freq)
+{
+#ifdef CONFIG_OCV
+ struct wpa_channel_info ci;
+
+ if (!wpa_auth_uses_ocv(sm))
+ return 0;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element");
+ return -1;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (freq) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %u MHz",
+ ci.frequency, freq);
+ ci.frequency = freq;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return ocv_insert_oci_kde(&ci, argpos);
+#else /* CONFIG_OCV */
+ return 0;
+#endif /* CONFIG_OCV */
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
+ const u8 *ie, size_t ie_len)
+{
+ const u8 *elem;
+ u8 *buf;
+
+ wpa_printf(MSG_DEBUG, "TESTING: %s EAPOL override", name);
+ wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie before override",
+ old_buf, *len);
+ buf = os_malloc(*len + ie_len);
+ if (!buf)
+ return NULL;
+ os_memcpy(buf, old_buf, *len);
+ elem = get_ie(buf, *len, eid);
+ if (elem) {
+ u8 elem_len = 2 + elem[1];
+
+ os_memmove((void *) elem, elem + elem_len,
+ *len - (elem - buf) - elem_len);
+ *len -= elem_len;
+ }
+ os_memcpy(buf + *len, ie, ie_len);
+ *len += ie_len;
+ wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie after EAPOL override",
+ buf, *len);
+
+ return buf;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_rsn_info *info)
+{
+ info->rsn_ies = a->wpa_ie;
+ info->rsn_ies_len = a->wpa_ie_len;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLD: link_id=%u, rsn_ies_len=%zu",
+ info->link_id, info->rsn_ies_len);
+}
+
+
+static void wpa_auth_get_ml_rsn_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_ml_rsn_info *info)
+{
+ if (!wpa_auth->cb->get_ml_rsn_info)
+ return;
+
+ wpa_auth->cb->get_ml_rsn_info(wpa_auth->cb_ctx, info);
+}
+
+
+void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_key_info *info,
+ bool mgmt_frame_prot, bool beacon_prot)
+{
+ struct wpa_group *gsm = a->group;
+ u8 rsc[WPA_KEY_RSC_LEN];
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u",
+ info->link_id, mgmt_frame_prot, beacon_prot);
+
+ info->gtkidx = gsm->GN & 0x03;
+ info->gtk = gsm->GTK[gsm->GN - 1];
+ info->gtk_len = gsm->GTK_len;
+
+ if (wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0)
+ os_memset(info->pn, 0, sizeof(info->pn));
+ else
+ os_memcpy(info->pn, rsc, sizeof(info->pn));
+
+ if (!mgmt_frame_prot)
+ return;
+
+ info->igtkidx = gsm->GN_igtk;
+ info->igtk = gsm->IGTK[gsm->GN_igtk - 4];
+ info->igtk_len = wpa_cipher_key_len(a->conf.group_mgmt_cipher);
+
+ if (wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0)
+ os_memset(info->ipn, 0, sizeof(info->ipn));
+ else
+ os_memcpy(info->ipn, rsc, sizeof(info->ipn));
+
+ if (!beacon_prot)
+ return;
+
+ info->bigtkidx = gsm->GN_bigtk;
+ info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
+
+ if (wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0)
+ os_memset(info->bipn, 0, sizeof(info->bipn));
+ else
+ os_memcpy(info->bipn, rsc, sizeof(info->bipn));
+}
+
+
+static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_ml_key_info *info)
+{
+ if (!wpa_auth->cb->get_ml_key_info)
+ return;
+
+ wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
+}
+
+
+static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+{
+ struct wpa_group *gsm = sm->group;
+ size_t gtk_len = gsm->GTK_len;
+ size_t igtk_len;
+ size_t kde_len;
+ unsigned int n_links;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ n_links = sm->n_mld_affiliated_links + 1;
+
+ /* MLO GTK KDE for each link */
+ kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
+
+ if (!sm->mgmt_frame_prot)
+ return kde_len;
+
+ /* MLO IGTK KDE for each link */
+ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+
+ if (!sm->wpa_auth->conf.beacon_prot)
+ return kde_len;
+
+ /* MLO BIGTK KDE for each link */
+ kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+
+ return kde_len;
+}
+
+
+static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_auth_ml_key_info ml_key_info;
+ unsigned int i, link_id;
+
+ /* First fetch the key information from all the authenticators */
+ os_memset(&ml_key_info, 0, sizeof(ml_key_info));
+ ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1;
+
+ /*
+ * Assume that management frame protection and beacon protection are the
+ * same on all links.
+ */
+ ml_key_info.mgmt_frame_prot = sm->mgmt_frame_prot;
+ ml_key_info.beacon_prot = sm->wpa_auth->conf.beacon_prot;
+
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ ml_key_info.links[i++].link_id = link_id;
+ }
+
+ wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info);
+
+ /* Add MLO GTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLO GTK: link=%u", link_id);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: MLO GTK",
+ ml_key_info.links[i].gtk,
+ ml_key_info.links[i].gtk_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 1 + 6 +
+ ml_key_info.links[i].gtk_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_GTK);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = (ml_key_info.links[i].gtkidx & 0x3) | (link_id << 4);
+
+ os_memcpy(pos, ml_key_info.links[i].pn, 6);
+ pos += 6;
+
+ os_memcpy(pos, ml_key_info.links[i].gtk,
+ ml_key_info.links[i].gtk_len);
+ pos += ml_key_info.links[i].gtk_len;
+
+ i++;
+ }
+
+ if (!sm->mgmt_frame_prot)
+ return pos;
+
+ /* Add MLO IGTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLO IGTK: link=%u", link_id);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: MLO IGTK",
+ ml_key_info.links[i].igtk,
+ ml_key_info.links[i].igtk_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 2 + 1 +
+ sizeof(ml_key_info.links[i].ipn) +
+ ml_key_info.links[i].igtk_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_IGTK);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Add the Key ID */
+ *pos++ = ml_key_info.links[i].igtkidx;
+ *pos++ = 0;
+
+ /* Add the IPN */
+ os_memcpy(pos, ml_key_info.links[i].ipn,
+ sizeof(ml_key_info.links[i].ipn));
+ pos += sizeof(ml_key_info.links[i].ipn);
+
+ *pos++ = ml_key_info.links[i].link_id << 4;
+
+ os_memcpy(pos, ml_key_info.links[i].igtk,
+ ml_key_info.links[i].igtk_len);
+ pos += ml_key_info.links[i].igtk_len;
+
+ i++;
+ }
+
+ if (!sm->wpa_auth->conf.beacon_prot)
+ return pos;
+
+ /* Add MLO BIGTK KDEs */
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLO BIGTK: link=%u", link_id);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: MLO BIGTK",
+ ml_key_info.links[i].bigtk,
+ ml_key_info.links[i].igtk_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 2 + 1 +
+ sizeof(ml_key_info.links[i].bipn) +
+ ml_key_info.links[i].igtk_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_BIGTK);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Add the Key ID */
+ *pos++ = ml_key_info.links[i].bigtkidx;
+ *pos++ = 0;
+
+ /* Add the BIPN */
+ os_memcpy(pos, ml_key_info.links[i].bipn,
+ sizeof(ml_key_info.links[i].bipn));
+ pos += sizeof(ml_key_info.links[i].bipn);
+
+ *pos++ = ml_key_info.links[i].link_id << 4;
+
+ os_memcpy(pos, ml_key_info.links[i].bigtk,
+ ml_key_info.links[i].igtk_len);
+ pos += ml_key_info.links[i].igtk_len;
+
+ i++;
+ }
+
+ return pos;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
+{
+ size_t kde_len = 0;
+
+#ifdef CONFIG_IEEE80211BE
+ unsigned int link_id;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ /* For the MAC Address KDE */
+ kde_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ /* MLO Link KDE for each link */
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+ sm->mld_links[link_id].rsne_len +
+ sm->mld_links[link_id].rsnxe_len;
+ }
+
+ kde_len += wpa_auth_ml_group_kdes_len(sm);
+#endif /* CONFIG_IEEE80211BE */
+
+ return kde_len;
+}
+
+
+static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+{
+#ifdef CONFIG_IEEE80211BE
+ u8 link_id;
+
+ if (sm->mld_assoc_link_id < 0)
+ return pos;
+
+ wpa_printf(MSG_DEBUG, "RSN: MLD: Adding MAC Address KDE");
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR,
+ sm->own_mld_addr, ETH_ALEN, NULL, 0);
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: MLO Link: link=%u, len=%zu", link_id,
+ RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+ sm->mld_links[link_id].rsne_len +
+ sm->mld_links[link_id].rsnxe_len);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+ sm->mld_links[link_id].rsne_len +
+ sm->mld_links[link_id].rsnxe_len;
+
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_LINK);
+ pos += RSN_SELECTOR_LEN;
+
+ /* Add the Link Information */
+ *pos = link_id;
+ if (sm->mld_links[link_id].rsne_len)
+ *pos |= RSN_MLO_LINK_KDE_LI_RSNE_INFO;
+ if (sm->mld_links[link_id].rsnxe_len)
+ *pos |= RSN_MLO_LINK_KDE_LI_RSNXE_INFO;
+
+ pos++;
+ os_memcpy(pos, sm->mld_links[link_id].own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ if (sm->mld_links[link_id].rsne_len) {
+ os_memcpy(pos, sm->mld_links[link_id].rsne,
+ sm->mld_links[link_id].rsne_len);
+ pos += sm->mld_links[link_id].rsne_len;
+ }
+
+ if (sm->mld_links[link_id].rsnxe_len) {
+ os_memcpy(pos, sm->mld_links[link_id].rsnxe,
+ sm->mld_links[link_id].rsnxe_len);
+ pos += sm->mld_links[link_id].rsnxe_len;
+ }
+ }
+
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+#endif /* CONFIG_IEEE80211BE */
+
+ return pos;
+}
+
+
+SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+{
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
+ size_t gtk_len, kde_len = 0, wpa_ie_len;
+ struct wpa_group *gsm = sm->group;
+ u8 *wpa_ie;
+ int secure, gtkidx, encr = 0;
+ u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
+ u8 hdr[2];
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+#ifdef CONFIG_IEEE80211BE
+ bool is_mld = sm->mld_assoc_link_id >= 0;
+#else /* CONFIG_IEEE80211BE */
+ bool is_mld = false;
+#endif /* CONFIG_IEEE80211BE */
+
+ SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
+ sm->TimeoutEvt = false;
+
+ sm->TimeoutCtr++;
+ if (conf->wpa_disable_eapol_key_retries && sm->TimeoutCtr > 1) {
+ /* Do not allow retransmission of EAPOL-Key msg 3/4 */
+ return;
+ }
+ if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+ GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
+ */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+ wpa_ie = sm->wpa_auth->wpa_ie;
+ wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+ if (sm->wpa == WPA_VERSION_WPA && (conf->wpa & WPA_PROTO_RSN) &&
+ wpa_ie_len > wpa_ie[1] + 2U && wpa_ie[0] == WLAN_EID_RSN) {
+ /* WPA-only STA, remove RSN IE and possible MDIE */
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_RSNX)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ wpa_ie_len = wpa_ie[1] + 2;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->rsne_override_eapol_set) {
+ wpa_ie_buf2 = replace_ie(
+ "RSNE", wpa_ie, &wpa_ie_len, WLAN_EID_RSN,
+ conf->rsne_override_eapol,
+ conf->rsne_override_eapol_len);
+ if (!wpa_ie_buf2)
+ goto done;
+ wpa_ie = wpa_ie_buf2;
+ }
+ if (conf->rsnxe_override_eapol_set) {
+ wpa_ie_buf = replace_ie(
+ "RSNXE", wpa_ie, &wpa_ie_len, WLAN_EID_RSNX,
+ conf->rsnxe_override_eapol,
+ conf->rsnxe_override_eapol_len);
+ if (!wpa_ie_buf)
+ goto done;
+ wpa_ie = wpa_ie_buf;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "sending 3/4 msg of 4-Way Handshake");
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ if (sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
+ wpa_auth_set_key(sm->wpa_auth, 0,
+ wpa_cipher_to_alg(sm->pairwise),
+ sm->addr,
+ sm->keyidx_active, sm->PTK.tk,
+ wpa_cipher_key_len(sm->pairwise),
+ KEY_FLAG_PAIRWISE_RX)) {
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to set LTF keyseed to driver");
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#endif /* CONFIG_PASN */
+
+ /* WPA2 send GTK in the 4-way handshake */
+ secure = 1;
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ if (conf->disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(stub_gtk, gtk_len) < 0)
+ goto done;
+ gtk = stub_gtk;
+ }
+ gtkidx = gsm->GN;
+ _rsc = rsc;
+ encr = 1;
+ } else {
+ /* WPA does not include GTK in msg 3/4 */
+ secure = 0;
+ gtk = NULL;
+ gtk_len = 0;
+ gtkidx = 0;
+ _rsc = NULL;
+ if (sm->rx_eapol_key_secure) {
+ /*
+ * It looks like Windows 7 supplicant tries to use
+ * Secure bit in msg 2/4 after having reported Michael
+ * MIC failure and it then rejects the 4-way handshake
+ * if msg 3/4 does not set Secure bit. Work around this
+ * by setting the Secure bit here even in the case of
+ * WPA if the supplicant used it first.
+ */
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
+ secure = 1;
+ }
+ }
+
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+ if (sm->use_ext_key_id)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
+ if (gtk)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+ kde_len += 300; /* FTIE + 2 * TIE */
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0)
+ kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
+
+ if (conf->transition_disable)
+ kde_len += 2 + RSN_SELECTOR_LEN + 1;
+
+#ifdef CONFIG_DPP2
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2;
+#endif /* CONFIG_DPP2 */
+
+ kde_len += wpa_auth_ml_kdes_len(sm);
+
+ kde = os_malloc(kde_len);
+ if (!kde)
+ goto done;
+
+ pos = kde;
+ if (!is_mld) {
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ size_t elen;
+
+ elen = pos - kde;
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
+ goto done;
+ }
+ pos -= wpa_ie_len;
+ pos += elen;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ hdr[1] = 0;
+
+ if (sm->use_ext_key_id) {
+ hdr[0] = sm->keyidx_active & 0x01;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+ }
+
+ if (gtk && !is_mld) {
+ hdr[0] = gtkidx & 0x03;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ }
+ pos = ieee80211w_kde_add(sm, pos);
+ if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0)
+ goto done;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+
+ if (sm->assoc_resp_ftie &&
+ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+ os_memcpy(pos, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]);
+ res = 2 + sm->assoc_resp_ftie[1];
+ } else {
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+ sm->xxkey_len,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, pos,
+ kde + kde_len - pos,
+ NULL, 0, 0);
+ }
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to insert FTIE into EAPOL-Key Key Data");
+ goto done;
+ }
+ pos += res;
+
+ /* TIE[ReassociationDeadline] (TU) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+ WPA_PUT_LE32(pos, conf->reassociation_deadline);
+ pos += 4;
+
+ /* TIE[KeyLifetime] (seconds) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+ pos += 4;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0) {
+ u8 addr[3 * 4];
+ os_memcpy(addr, sm->ip_addr, 4);
+ os_memcpy(addr + 4, conf->ip_addr_mask, 4);
+ os_memcpy(addr + 8, conf->ip_addr_go, 4);
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+ addr, sizeof(addr), NULL, 0);
+ }
+#endif /* CONFIG_P2P */
+
+ if (conf->transition_disable)
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_TRANSITION_DISABLE,
+ &conf->transition_disable, 1, NULL, 0);
+
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+ u8 payload[2];
+
+ payload[0] = DPP_VERSION; /* Protocol Version */
+ payload[1] = 0; /* Flags */
+ if (conf->dpp_pfs == 0)
+ payload[1] |= DPP_KDE_PFS_ALLOWED;
+ else if (conf->dpp_pfs == 1)
+ payload[1] |= DPP_KDE_PFS_ALLOWED |
+ DPP_KDE_PFS_REQUIRED;
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_DPP,
+ payload, sizeof(payload), NULL, 0);
+ }
+#endif /* CONFIG_DPP2 */
+
+ pos = wpa_auth_ml_kdes(sm, pos);
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ (secure ? WPA_KEY_INFO_SECURE : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_KEY_TYPE,
+ _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+done:
+ bin_clear_free(kde, kde_len);
+ os_free(wpa_ie_buf);
+ os_free(wpa_ie_buf2);
+}
+
+
+static int wpa_auth_validate_ml_kdes_m4(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ const struct ieee802_1x_hdr *hdr;
+ const struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
+ const u8 *key_data, *mic;
+ u16 key_data_length;
+ size_t mic_len;
+
+ if (sm->mld_assoc_link_id < 0)
+ return 0;
+
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+ hdr = (const struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (const struct wpa_eapol_key *) (hdr + 1);
+ mic = (const u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+ sizeof(*key) - mic_len - 2)
+ return -1;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key msg 4/4 with invalid Key Data contents");
+ return -1;
+ }
+
+ /* MLD MAC address must be the same */
+ if (!kde.mac_addr ||
+ os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: MLD address in EAPOL-Key msg 4/4: " MACSTR,
+ MAC2STR(kde.mac_addr));
+#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKINITDONE)
+{
+ SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
+ sm->EAPOLKeyReceived = false;
+
+ if (wpa_auth_validate_ml_kdes_m4(sm) < 0) {
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
+ if (sm->Pair) {
+ enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
+ int klen = wpa_cipher_key_len(sm->pairwise);
+ int res;
+
+ if (sm->use_ext_key_id)
+ res = wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
+ sm->keyidx_active, NULL, 0,
+ KEY_FLAG_PAIRWISE_RX_TX_MODIFY);
+ else
+ res = wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr,
+ 0, sm->PTK.tk, klen,
+ KEY_FLAG_PAIRWISE_RX_TX);
+ if (res) {
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to set LTF keyseed to driver");
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#endif /* CONFIG_PASN */
+
+ /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+ sm->pairwise_set = true;
+
+ wpa_auth_set_ptk_rekey_timer(sm);
+ wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+ dot11RSNAConfigPMKLifetime, &sm->PTK);
+
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_authorized, 1);
+ }
+ }
+
+ if (0 /* IBSS == TRUE */) {
+ sm->keycount++;
+ if (sm->keycount == 2) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_portValid, 1);
+ }
+ } else {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
+ 1);
+ }
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable,
+ false);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, true);
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = true;
+ else
+ sm->has_GTK = true;
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "pairwise key handshake completed (%s)",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO, "EAPOL-4WAY-HS-COMPLETED "
+ MACSTR, MAC2STR(sm->addr));
+
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_push_pmk_r1(sm->wpa_auth, wpa_auth_get_spa(sm));
+#endif /* CONFIG_IEEE80211R_AP */
+
+ sm->ptkstart_without_success = 0;
+}
+
+
+SM_STEP(WPA_PTK)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+
+ if (sm->Init)
+ SM_ENTER(WPA_PTK, INITIALIZE);
+ else if (sm->Disconnect
+ /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "WPA_PTK: sm->Disconnect");
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ }
+ else if (sm->DeauthenticationRequest)
+ SM_ENTER(WPA_PTK, DISCONNECTED);
+ else if (sm->AuthenticationRequest)
+ SM_ENTER(WPA_PTK, AUTHENTICATION);
+ else if (sm->ReAuthenticationRequest)
+ SM_ENTER(WPA_PTK, AUTHENTICATION2);
+ else if (sm->PTKRequest) {
+ if (wpa_auth_sm_ptk_update(sm) < 0)
+ SM_ENTER(WPA_PTK, DISCONNECTED);
+ else
+ SM_ENTER(WPA_PTK, PTKSTART);
+ } else switch (sm->wpa_ptk_state) {
+ case WPA_PTK_INITIALIZE:
+ break;
+ case WPA_PTK_DISCONNECT:
+ SM_ENTER(WPA_PTK, DISCONNECTED);
+ break;
+ case WPA_PTK_DISCONNECTED:
+ SM_ENTER(WPA_PTK, INITIALIZE);
+ break;
+ case WPA_PTK_AUTHENTICATION:
+ SM_ENTER(WPA_PTK, AUTHENTICATION2);
+ break;
+ case WPA_PTK_AUTHENTICATION2:
+ if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+ wpa_auth_get_eapol(wpa_auth, sm->addr,
+ WPA_EAPOL_keyRun))
+ SM_ENTER(WPA_PTK, INITPMK);
+ else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
+ /* FIX: && 802.1X::keyRun */)
+ SM_ENTER(WPA_PTK, INITPSK);
+ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+ SM_ENTER(WPA_PTK, INITPMK);
+ break;
+ case WPA_PTK_INITPMK:
+ if (wpa_auth_get_eapol(wpa_auth, sm->addr,
+ WPA_EAPOL_keyAvailable)) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#ifdef CONFIG_DPP
+ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_DPP */
+ } else {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "INITPMK - keyAvailable = false");
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ }
+ break;
+ case WPA_PTK_INITPSK:
+ if (wpa_auth_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+ NULL, NULL, NULL)) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#ifdef CONFIG_SAE
+ } else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_SAE */
+ } else if (wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+ wpa_auth->conf.radius_psk) {
+ wpa_printf(MSG_DEBUG,
+ "INITPSK: No PSK yet available for STA - use RADIUS later");
+ SM_ENTER(WPA_PTK, PTKSTART);
+ } else {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "no PSK configured for the STA");
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ }
+ break;
+ case WPA_PTK_PTKSTART:
+ if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PTKSTART: Retry limit %u reached",
+ conf->wpa_pairwise_update_count);
+ sm->disconnect_reason =
+ WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ } else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKSTART);
+ break;
+ case WPA_PTK_PTKCALCNEGOTIATING:
+ if (sm->MICVerified)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
+ else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKSTART);
+ break;
+ case WPA_PTK_PTKCALCNEGOTIATING2:
+ SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+ break;
+ case WPA_PTK_PTKINITNEGOTIATING:
+ if (sm->update_snonce)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK, PTKINITDONE);
+ else if (sm->TimeoutCtr >
+ conf->wpa_pairwise_update_count ||
+ (conf->wpa_disable_eapol_key_retries &&
+ sm->TimeoutCtr > 1)) {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PTKINITNEGOTIATING: Retry limit %u reached",
+ conf->wpa_pairwise_update_count);
+ sm->disconnect_reason =
+ WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ } else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+ break;
+ case WPA_PTK_PTKINITDONE:
+ break;
+ }
+}
+
+
+SM_STATE(WPA_PTK_GROUP, IDLE)
+{
+ SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
+ if (sm->Init) {
+ /* Init flag is not cleared here, so avoid busy
+ * loop by claiming nothing changed. */
+ sm->changed = false;
+ }
+ sm->GTimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
+{
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_group *gsm = sm->group;
+ const u8 *kde = NULL;
+ u8 *kde_buf = NULL, *pos, hdr[2];
+ size_t kde_len = 0;
+ u8 *gtk, stub_gtk[32];
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ bool is_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ is_mld = sm->mld_assoc_link_id >= 0;
+#endif /* CONFIG_IEEE80211BE */
+
+ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+ sm->GTimeoutCtr++;
+ if (conf->wpa_disable_eapol_key_retries && sm->GTimeoutCtr > 1) {
+ /* Do not allow retransmission of EAPOL-Key group msg 1/2 */
+ return;
+ }
+ if (sm->GTimeoutCtr > conf->wpa_group_update_count) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = false;
+ sm->TimeoutEvt = false;
+ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "sending 1/2 msg of Group Key Handshake");
+
+ gtk = gsm->GTK[gsm->GN - 1];
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(stub_gtk, gsm->GTK_len) < 0)
+ return;
+ gtk = stub_gtk;
+ }
+
+ if (sm->wpa == WPA_VERSION_WPA2 && !is_mld) {
+ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+ kde_buf = os_malloc(kde_len);
+ if (!kde_buf)
+ return;
+
+ kde = pos = kde_buf;
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gsm->GTK_len);
+ pos = ieee80211w_kde_add(sm, pos);
+ if (ocv_oci_add(sm, &pos,
+ conf->oci_freq_override_eapol_g1) < 0) {
+ os_free(kde_buf);
+ return;
+ }
+ kde_len = pos - kde;
+#ifdef CONFIG_IEEE80211BE
+ } else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
+ kde_len = wpa_auth_ml_group_kdes_len(sm);
+ if (kde_len) {
+ kde_buf = os_malloc(kde_len);
+ if (!kde_buf)
+ return;
+
+ kde = pos = kde_buf;
+ wpa_auth_ml_group_kdes(sm, pos);
+ }
+#endif /* CONFIG_IEEE80211BE */
+ } else {
+ kde = gtk;
+ kde_len = gsm->GTK_len;
+ }
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_SECURE |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK |
+ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+ rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+ bin_clear_free(kde_buf, kde_len);
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+#ifdef CONFIG_OCV
+ const u8 *key_data, *mic;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
+ size_t mic_len;
+ u16 key_data_length;
+#endif /* CONFIG_OCV */
+
+ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+ sm->EAPOLKeyReceived = false;
+
+#ifdef CONFIG_OCV
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+ sizeof(*key) - mic_len - 2)
+ return;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "received EAPOL-Key group msg 2/2 with invalid Key Data contents");
+ return;
+ }
+
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(wpa_auth, &ci) != 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group 2/2");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) !=
+ OCI_SUCCESS) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "OCV failed: %s", ocv_errorstr);
+ if (wpa_auth->conf.msg_ctx)
+ wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
+ OCV_FAILURE "addr=" MACSTR
+ " frame=eapol-key-g2 error=%s",
+ MAC2STR(wpa_auth_get_spa(sm)),
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+ sm->GTimeoutCtr = 0;
+ /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "group key handshake completed (%s)",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+ sm->has_GTK = true;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, KEYERROR)
+{
+ SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+ sm->Disconnect = true;
+ sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+ "group key handshake failed (%s) after %u tries",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
+ sm->wpa_auth->conf.wpa_group_update_count);
+}
+
+
+SM_STEP(WPA_PTK_GROUP)
+{
+ if (sm->Init || sm->PtkGroupInit) {
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ sm->PtkGroupInit = false;
+ } else switch (sm->wpa_ptk_group_state) {
+ case WPA_PTK_GROUP_IDLE:
+ if (sm->GUpdateStationKeys ||
+ (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+ break;
+ case WPA_PTK_GROUP_REKEYNEGOTIATING:
+ if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ !sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+ else if (sm->GTimeoutCtr >
+ sm->wpa_auth->conf.wpa_group_update_count ||
+ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->GTimeoutCtr > 1))
+ SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+ else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+ break;
+ case WPA_PTK_GROUP_KEYERROR:
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ break;
+ case WPA_PTK_GROUP_REKEYESTABLISHED:
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ break;
+ }
+}
+
+
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ int ret = 0;
+ size_t len;
+
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->GTK[group->GN - 1], group->GTK_len) < 0)
+ ret = -1;
+ wpa_hexdump_key(MSG_DEBUG, "GTK",
+ group->GTK[group->GN - 1], group->GTK_len);
+
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->IGTK[group->GN_igtk - 4], len) < 0)
+ ret = -1;
+ wpa_hexdump_key(MSG_DEBUG, "IGTK",
+ group->IGTK[group->GN_igtk - 4], len);
+ }
+
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ conf->beacon_prot) {
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->BIGTK[group->GN_bigtk - 6], len) < 0)
+ ret = -1;
+ wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+ group->BIGTK[group->GN_bigtk - 6], len);
+ }
+
+ return ret;
+}
+
+
+static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state GTK_INIT (VLAN-ID %d)",
+ group->vlan_id);
+ group->changed = false; /* GInit is not cleared here; avoid loop */
+ group->wpa_group_state = WPA_GROUP_GTK_INIT;
+
+ /* GTK[0..N] = 0 */
+ os_memset(group->GTK, 0, sizeof(group->GTK));
+ group->GN = 1;
+ group->GM = 2;
+ group->GN_igtk = 4;
+ group->GM_igtk = 5;
+ group->GN_bigtk = 6;
+ group->GM_bigtk = 7;
+ /* GTK[GN] = CalcGTK() */
+ wpa_gtk_update(wpa_auth, group);
+}
+
+
+static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
+{
+ if (ctx != NULL && ctx != sm->group)
+ return 0;
+
+ if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Not in PTKINITDONE; skip Group Key update");
+ sm->GUpdateStationKeys = false;
+ return 0;
+ }
+ if (sm->GUpdateStationKeys) {
+ /*
+ * This should not really happen, so add a debug log entry.
+ * Since we clear the GKeyDoneStations before the loop, the
+ * station needs to be counted here anyway.
+ */
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "GUpdateStationKeys was already set when marking station for GTK rekeying");
+ }
+
+ /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
+ if (sm->is_wnmsleep)
+ return 0;
+
+ sm->group->GKeyDoneStations++;
+ sm->GUpdateStationKeys = true;
+
+ wpa_sm_step(sm);
+ return 0;
+}
+
+
+#ifdef CONFIG_WNM_AP
+/* update GTK when exiting WNM-Sleep Mode */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+{
+ if (!sm || sm->is_wnmsleep)
+ return;
+
+ wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+ if (sm)
+ sm->is_wnmsleep = !!flag;
+}
+
+
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_group *gsm = sm->group;
+ u8 *start = pos;
+
+ /*
+ * GTK subelement:
+ * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+ * Key[5..32]
+ */
+ *pos++ = WNM_SLEEP_SUBELEM_GTK;
+ *pos++ = 11 + gsm->GTK_len;
+ /* Key ID in B0-B1 of Key Info */
+ WPA_PUT_LE16(pos, gsm->GN & 0x03);
+ pos += 2;
+ *pos++ = gsm->GTK_len;
+ if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
+ return 0;
+ pos += 8;
+ os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(pos, gsm->GTK_len) < 0)
+ return 0;
+ }
+ pos += gsm->GTK_len;
+
+ wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+ gsm->GN);
+ wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
+ gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+
+ return pos - start;
+}
+
+
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_group *gsm = sm->group;
+ u8 *start = pos;
+ size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+ /*
+ * IGTK subelement:
+ * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+ */
+ *pos++ = WNM_SLEEP_SUBELEM_IGTK;
+ *pos++ = 2 + 6 + len;
+ WPA_PUT_LE16(pos, gsm->GN_igtk);
+ pos += 2;
+ if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
+ return 0;
+ pos += 6;
+
+ os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use
+ * of IGTK in the BSS.
+ */
+ if (random_get_bytes(pos, len) < 0)
+ return 0;
+ }
+ pos += len;
+
+ wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
+ gsm->GN_igtk);
+ wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
+ gsm->IGTK[gsm->GN_igtk - 4], len);
+
+ return pos - start;
+}
+
+
+int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_group *gsm = sm->group;
+ u8 *start = pos;
+ size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+ /*
+ * BIGTK subelement:
+ * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+ */
+ *pos++ = WNM_SLEEP_SUBELEM_BIGTK;
+ *pos++ = 2 + 6 + len;
+ WPA_PUT_LE16(pos, gsm->GN_bigtk);
+ pos += 2;
+ if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+ return 0;
+ pos += 6;
+
+ os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random BIGTK to each STA to prevent use
+ * of BIGTK in the BSS.
+ */
+ if (random_get_bytes(pos, len) < 0)
+ return 0;
+ }
+ pos += len;
+
+ wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
+ gsm->GN_bigtk);
+ wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit",
+ gsm->BIGTK[gsm->GN_bigtk - 6], len);
+
+ return pos - start;
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ int tmp;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+ group->vlan_id);
+ group->changed = true;
+ group->wpa_group_state = WPA_GROUP_SETKEYS;
+ group->GTKReKey = false;
+ tmp = group->GM;
+ group->GM = group->GN;
+ group->GN = tmp;
+ tmp = group->GM_igtk;
+ group->GM_igtk = group->GN_igtk;
+ group->GN_igtk = tmp;
+ tmp = group->GM_bigtk;
+ group->GM_bigtk = group->GN_bigtk;
+ group->GN_bigtk = tmp;
+ /* "GKeyDoneStations = GNoStations" is done in more robust way by
+ * counting the STAs that are marked with GUpdateStationKeys instead of
+ * including all STAs that could be in not-yet-completed state. */
+ wpa_gtk_update(wpa_auth, group);
+
+ if (group->GKeyDoneStations) {
+ wpa_printf(MSG_DEBUG,
+ "wpa_group_setkeys: Unexpected GKeyDoneStations=%d when starting new GTK rekey",
+ group->GKeyDoneStations);
+ group->GKeyDoneStations = 0;
+ }
+ wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
+ wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
+ group->GKeyDoneStations);
+}
+
+
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ int ret = 0;
+
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+ wpa_cipher_to_alg(conf->wpa_group),
+ broadcast_ether_addr, group->GN,
+ group->GTK[group->GN - 1], group->GTK_len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ ret = -1;
+
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ enum wpa_alg alg;
+ size_t len;
+
+ alg = wpa_cipher_to_alg(conf->group_mgmt_cipher);
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+
+ if (ret == 0 &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ broadcast_ether_addr, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4], len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ ret = -1;
+
+ if (ret == 0 && conf->beacon_prot &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ broadcast_ether_addr, group->GN_bigtk,
+ group->BIGTK[group->GN_bigtk - 6], len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+ if (sm->group == ctx) {
+ wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+ " for disconnection due to fatal failure",
+ MAC2STR(wpa_auth_get_spa(sm)));
+ sm->Disconnect = true;
+ }
+
+ return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state FATAL_FAILURE");
+ group->changed = true;
+ group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+ wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state SETKEYSDONE (VLAN-ID %d)",
+ group->vlan_id);
+ group->changed = true;
+ group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
+
+ if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+ wpa_group_fatal_failure(wpa_auth, group);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ if (group->GInit) {
+ wpa_group_gtk_init(wpa_auth, group);
+ } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
+ /* Do not allow group operations */
+ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
+ group->GTKAuthenticator) {
+ wpa_group_setkeysdone(wpa_auth, group);
+ } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
+ group->GTKReKey) {
+ wpa_group_setkeys(wpa_auth, group);
+ } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) {
+ if (group->GKeyDoneStations == 0)
+ wpa_group_setkeysdone(wpa_auth, group);
+ else if (group->GTKReKey)
+ wpa_group_setkeys(wpa_auth, group);
+ }
+}
+
+
+static int wpa_sm_step(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return 0;
+
+ if (sm->in_step_loop) {
+ /* This should not happen, but if it does, make sure we do not
+ * end up freeing the state machine too early by exiting the
+ * recursive call. */
+ wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively");
+ return 0;
+ }
+
+ sm->in_step_loop = 1;
+ do {
+ if (sm->pending_deinit)
+ break;
+
+ sm->changed = false;
+ sm->wpa_auth->group->changed = false;
+
+ SM_STEP_RUN(WPA_PTK);
+ if (sm->pending_deinit)
+ break;
+ SM_STEP_RUN(WPA_PTK_GROUP);
+ if (sm->pending_deinit)
+ break;
+ wpa_group_sm_step(sm->wpa_auth, sm->group);
+ } while (sm->changed || sm->wpa_auth->group->changed);
+ sm->in_step_loop = 0;
+
+ if (sm->pending_deinit) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Completing pending STA state machine deinit for "
+ MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
+ wpa_free_sta_sm(sm);
+ return 1;
+ }
+ return 0;
+}
+
+
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = eloop_ctx;
+ wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sm_notify(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return;
+ eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
+
+
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
+{
+ int tmp, i;
+ struct wpa_group *group;
+
+ if (!wpa_auth)
+ return;
+
+ group = wpa_auth->group;
+
+ for (i = 0; i < 2; i++) {
+ tmp = group->GM;
+ group->GM = group->GN;
+ group->GN = tmp;
+ tmp = group->GM_igtk;
+ group->GM_igtk = group->GN_igtk;
+ group->GN_igtk = tmp;
+ tmp = group->GM_bigtk;
+ group->GM_bigtk = group->GN_bigtk;
+ group->GN_bigtk = tmp;
+ wpa_gtk_update(wpa_auth, group);
+ wpa_group_config_group_keys(wpa_auth, group);
+ }
+}
+
+
+static const char * wpa_bool_txt(int val)
+{
+ return val ? "TRUE" : "FALSE";
+}
+
+#ifdef CONFIG_CTRL_IFACE_MIB
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
+{
+ struct wpa_auth_config *conf;
+ int len = 0, ret;
+ char pmkid_txt[PMKID_LEN * 2 + 1];
+#ifdef CONFIG_RSN_PREAUTH
+ const int preauth = 1;
+#else /* CONFIG_RSN_PREAUTH */
+ const int preauth = 0;
+#endif /* CONFIG_RSN_PREAUTH */
+
+ if (!wpa_auth)
+ return len;
+ conf = &wpa_auth->conf;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot11RSNAOptionImplemented=TRUE\n"
+ "dot11RSNAPreauthenticationImplemented=%s\n"
+ "dot11RSNAEnabled=%s\n"
+ "dot11RSNAPreauthenticationEnabled=%s\n",
+ wpa_bool_txt(preauth),
+ wpa_bool_txt(conf->wpa & WPA_PROTO_RSN),
+ wpa_bool_txt(conf->rsn_preauth));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+ wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN);
+
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "dot11RSNAConfigVersion=%u\n"
+ "dot11RSNAConfigPairwiseKeysSupported=9999\n"
+ /* FIX: dot11RSNAConfigGroupCipher */
+ /* FIX: dot11RSNAConfigGroupRekeyMethod */
+ /* FIX: dot11RSNAConfigGroupRekeyTime */
+ /* FIX: dot11RSNAConfigGroupRekeyPackets */
+ "dot11RSNAConfigGroupRekeyStrict=%u\n"
+ "dot11RSNAConfigGroupUpdateCount=%u\n"
+ "dot11RSNAConfigPairwiseUpdateCount=%u\n"
+ "dot11RSNAConfigGroupCipherSize=%u\n"
+ "dot11RSNAConfigPMKLifetime=%u\n"
+ "dot11RSNAConfigPMKReauthThreshold=%u\n"
+ "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
+ "dot11RSNAConfigSATimeout=%u\n"
+ "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAPMKIDUsed=%s\n"
+ "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNATKIPCounterMeasuresInvoked=%u\n"
+ "dot11RSNA4WayHandshakeFailures=%u\n"
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+ RSN_VERSION,
+ !!conf->wpa_strict_rekey,
+ conf->wpa_group_update_count,
+ conf->wpa_pairwise_update_count,
+ wpa_cipher_key_len(conf->wpa_group) * 8,
+ dot11RSNAConfigPMKLifetime,
+ dot11RSNAConfigPMKReauthThreshold,
+ dot11RSNAConfigSATimeout,
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
+ pmkid_txt,
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
+ wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
+ wpa_auth->dot11RSNA4WayHandshakeFailures);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* TODO: dot11RSNAConfigPairwiseCiphersTable */
+ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */
+
+ /* Private MIB */
+ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
+ wpa_auth->group->wpa_group_state);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+{
+ int len = 0, ret;
+ u32 pairwise = 0;
+
+ if (!sm)
+ return 0;
+
+ /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
+
+ /* dot11RSNAStatsEntry */
+
+ pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+ WPA_PROTO_RSN : WPA_PROTO_WPA,
+ sm->pairwise);
+ if (pairwise == 0)
+ return 0;
+
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ /* TODO: dot11RSNAStatsIndex */
+ "dot11RSNAStatsSTAAddress=" MACSTR "\n"
+ "dot11RSNAStatsVersion=1\n"
+ "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
+ /* TODO: dot11RSNAStatsTKIPICVErrors */
+ "dot11RSNAStatsTKIPLocalMICFailures=%u\n"
+ "dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
+ /* TODO: dot11RSNAStatsCCMPReplays */
+ /* TODO: dot11RSNAStatsCCMPDecryptErrors */
+ /* TODO: dot11RSNAStatsTKIPReplays */,
+ MAC2STR(sm->addr),
+ RSN_SUITE_ARG(pairwise),
+ sm->dot11RSNAStatsTKIPLocalMICFailures,
+ sm->dot11RSNAStatsTKIPRemoteMICFailures);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* Private MIB */
+ ret = os_snprintf(buf + len, buflen - len,
+ "wpa=%d\n"
+ "AKMSuiteSelector=" RSN_SUITE "\n"
+ "hostapdWPAPTKState=%d\n"
+ "hostapdWPAPTKGroupState=%d\n"
+ "hostapdMFPR=%d\n",
+ sm->wpa,
+ RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
+ sm->wpa_ptk_state,
+ sm->wpa_ptk_group_state,
+ sm->mfpr);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ return len;
+}
+#endif
+
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+{
+ if (wpa_auth)
+ wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
+}
+
+
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm)
+{
+ return sm && sm->pairwise_set;
+}
+
+
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
+{
+ return sm->pairwise;
+}
+
+
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
+{
+ if (!sm)
+ return NULL;
+ *len = sm->pmk_len;
+ return sm->PMK;
+}
+
+
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+ if (!sm || !sm->pmksa)
+ return NULL;
+ return sm->pmksa->dpp_pkhash;
+}
+
+
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return -1;
+ return sm->wpa_key_mgmt;
+}
+
+
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return 0;
+ return sm->wpa;
+}
+
+
+int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm)
+{
+ if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+ return 0;
+ return sm->tk_already_set;
+}
+
+
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
+{
+ if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+ return 0;
+ return sm->tk_already_set;
+}
+
+
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ if (!sm || sm->pmksa != entry)
+ return -1;
+ sm->pmksa = NULL;
+ return 0;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm)
+{
+ return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm)
+{
+ if (sm)
+ sm->dot11RSNAStatsTKIPLocalMICFailures++;
+}
+
+
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
+{
+ if (!wpa_auth)
+ return NULL;
+ *len = wpa_auth->wpa_ie_len;
+ return wpa_auth->wpa_ie;
+}
+
+
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+ unsigned int pmk_len,
+ int session_timeout, struct eapol_state_machine *eapol)
+{
+ if (!sm || sm->wpa != WPA_VERSION_WPA2 ||
+ sm->wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+ wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ /* Cache MPMK/XXKey instead of initial part from MSK */
+ pmk = pmk + PMK_LEN;
+ pmk_len = PMK_LEN;
+ } else
+#endif /* CONFIG_IEEE80211R_AP */
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ if (pmk_len > PMK_LEN_SUITE_B_192)
+ pmk_len = PMK_LEN_SUITE_B_192;
+ } else if (pmk_len > PMK_LEN) {
+ pmk_len = PMK_LEN;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len);
+ if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
+ sm->PTK.kck, sm->PTK.kck_len,
+ wpa_auth_get_aa(sm),
+ wpa_auth_get_spa(sm), session_timeout,
+ eapol, sm->wpa_key_mgmt))
+ return 0;
+
+ return -1;
+}
+
+
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+ const u8 *pmk, size_t len, const u8 *sta_addr,
+ int session_timeout,
+ struct eapol_state_machine *eapol)
+{
+ if (!wpa_auth)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len);
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
+ NULL, 0,
+ wpa_auth->addr,
+ sta_addr, session_timeout, eapol,
+ WPA_KEY_MGMT_IEEE8021X))
+ return 0;
+
+ return -1;
+}
+
+
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int akmp)
+{
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
+ if (!akmp)
+ akmp = WPA_KEY_MGMT_SAE;
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
+ return 0;
+
+ return -1;
+}
+
+
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
+{
+ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+ sm->pmkid_set = 1;
+}
+
+
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp)
+{
+ if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, session_timeout,
+ NULL, akmp))
+ return 0;
+
+ return -1;
+}
+
+
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp, const u8 *dpp_pkhash)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
+ entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, session_timeout,
+ NULL, akmp);
+ if (!entry)
+ return -1;
+
+ if (dpp_pkhash)
+ entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+{
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return;
+ pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+ if (pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
+ MACSTR " based on request", MAC2STR(sta_addr));
+ pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
+ }
+}
+
+
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+ size_t len)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return 0;
+ return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+}
+
+
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
+{
+ if (wpa_auth && wpa_auth->pmksa)
+ pmksa_cache_auth_flush(wpa_auth->pmksa);
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ char *buf, size_t len)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return 0;
+
+ return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ size_t pmk_len, int akmp,
+ const u8 *pmkid, int expiration)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, NULL, 0, aa,
+ spa, 0, NULL, akmp);
+ if (!entry)
+ return NULL;
+
+ os_get_reltime(&now);
+ entry->expiration = now.sec + expiration;
+ return entry;
+}
+
+
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ int ret;
+
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return -1;
+
+ ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
+ if (ret < 0)
+ wpa_printf(MSG_DEBUG,
+ "RSN: Failed to store external PMKSA cache for "
+ MACSTR, MAC2STR(entry->spa));
+
+ return ret;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return NULL;
+ return wpa_auth->pmksa;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *pmkid)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return NULL;
+ return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
+}
+
+
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+ struct wpa_state_machine *sm,
+ struct wpa_authenticator *wpa_auth,
+ u8 *pmkid, u8 *pmk)
+{
+ if (!sm)
+ return;
+
+ sm->pmksa = pmksa;
+ os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+ os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
+ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
+}
+
+
+/*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+ * gets modified.
+ */
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ struct wpa_group *prev = wpa_auth->group;
+
+ wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
+ group->vlan_id);
+
+ while (prev) {
+ if (prev->next == group) {
+ /* This never frees the special first group as needed */
+ prev->next = group->next;
+ os_free(group);
+ break;
+ }
+ prev = prev->next;
+ }
+
+}
+
+
+/* Increase the reference counter for group */
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ /* Skip the special first group */
+ if (wpa_auth->group == group)
+ return;
+
+ group->references++;
+}
+
+
+/* Decrease the reference counter and maybe free the group */
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ /* Skip the special first group */
+ if (wpa_auth->group == group)
+ return;
+
+ group->references--;
+ if (group->references)
+ return;
+ wpa_group_free(wpa_auth, group);
+}
+
+
+/*
+ * Add a group that has its references counter set to zero. Caller needs to
+ * call wpa_group_get() on the return value to mark the entry in use.
+ */
+static struct wpa_group *
+wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (!wpa_auth || !wpa_auth->group)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
+ vlan_id);
+ group = wpa_group_init(wpa_auth, vlan_id, 0);
+ if (!group)
+ return NULL;
+
+ group->next = wpa_auth->group->next;
+ wpa_auth->group->next = group;
+
+ return group;
+}
+
+
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (!wpa_auth)
+ return 0;
+
+ group = wpa_auth->group;
+ while (group) {
+ if (group->vlan_id == vlan_id)
+ break;
+ group = group->next;
+ }
+
+ if (!group) {
+ group = wpa_auth_add_group(wpa_auth, vlan_id);
+ if (!group)
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Ensure group state machine running for VLAN ID %d",
+ vlan_id);
+
+ wpa_group_get(wpa_auth, group);
+ group->num_setup_iface++;
+
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Decrease reference counter, expected to be zero afterwards.
+ * returns: -1 on error (group not found, group in fail state)
+ * -2 if wpa_group is still referenced
+ * 0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+ struct wpa_group *group;
+ int ret = 0;
+
+ if (!wpa_auth)
+ return 0;
+
+ group = wpa_auth->group;
+ while (group) {
+ if (group->vlan_id == vlan_id)
+ break;
+ group = group->next;
+ }
+
+ if (!group)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Try stopping group state machine for VLAN ID %d",
+ vlan_id);
+
+ if (group->num_setup_iface <= 0) {
+ wpa_printf(MSG_ERROR,
+ "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
+ vlan_id);
+ return -1;
+ }
+ group->num_setup_iface--;
+
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ ret = -1;
+
+ if (group->references > 1) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
+ vlan_id);
+ ret = -2;
+ }
+
+ wpa_group_put(wpa_auth, group);
+
+ return ret;
+}
+
+
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (!sm || !sm->wpa_auth)
+ return 0;
+
+ group = sm->wpa_auth->group;
+ while (group) {
+ if (group->vlan_id == vlan_id)
+ break;
+ group = group->next;
+ }
+
+ if (!group) {
+ group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
+ if (!group)
+ return -1;
+ }
+
+ if (sm->group == group)
+ return 0;
+
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR
+ " to use group state machine for VLAN ID %d",
+ MAC2STR(wpa_auth_get_spa(sm)), vlan_id);
+
+ wpa_group_get(sm->wpa_auth, group);
+ wpa_group_put(sm->wpa_auth, sm->group);
+ sm->group = group;
+
+ return 0;
+}
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack)
+{
+ if (!wpa_auth || !sm)
+ return;
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+ " ack=%d", MAC2STR(wpa_auth_get_spa(sm)), ack);
+ if (sm->pending_1_of_4_timeout && ack) {
+ /*
+ * Some deployed supplicant implementations update their SNonce
+ * for each EAPOL-Key 2/4 message even within the same 4-way
+ * handshake and then fail to use the first SNonce when
+ * deriving the PTK. This results in unsuccessful 4-way
+ * handshake whenever the relatively short initial timeout is
+ * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+ * around this by increasing the timeout now that we know that
+ * the station has received the frame.
+ */
+ int timeout_ms = eapol_key_timeout_subseq;
+ wpa_printf(MSG_DEBUG,
+ "WPA: Increase initial EAPOL-Key 1/4 timeout by %u ms because of acknowledged frame",
+ timeout_ms);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ eloop_register_timeout(timeout_ms / 1000,
+ (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, wpa_auth, sm);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->eapol_status_cb) {
+ sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
+ sm->eapol_status_cb_ctx2);
+ sm->eapol_status_cb = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return 0;
+ return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
+}
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+ if (!sm)
+ return 0;
+ return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+ if (!sm || WPA_GET_BE32(sm->ip_addr) == 0)
+ return -1;
+ os_memcpy(addr, sm->ip_addr, 4);
+ return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+ struct radius_das_attrs *attr)
+{
+ return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group;
+
+ if (!wpa_auth)
+ return;
+ for (group = wpa_auth->group; group; group = group->next)
+ wpa_group_config_group_keys(wpa_auth, group);
+}
+
+
+#ifdef CONFIG_FILS
+
+struct wpa_auth_fils_iter_data {
+ struct wpa_authenticator *auth;
+ const u8 *cache_id;
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *spa;
+ const u8 *pmkid;
+};
+
+
+static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
+{
+ struct wpa_auth_fils_iter_data *data = ctx;
+
+ if (a == data->auth || !a->conf.fils_cache_id_set ||
+ os_memcmp(a->conf.fils_cache_id, data->cache_id,
+ FILS_CACHE_ID_LEN) != 0)
+ return 0;
+ data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+ return data->pmksa != NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 *pmkid)
+{
+ struct wpa_auth_fils_iter_data idata;
+
+ if (!wpa_auth->conf.fils_cache_id_set)
+ return NULL;
+ idata.auth = wpa_auth;
+ idata.cache_id = wpa_auth->conf.fils_cache_id;
+ idata.pmksa = NULL;
+ idata.spa = sta_addr;
+ idata.pmkid = pmkid;
+ wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
+ return idata.pmksa;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ u8 *buf, size_t len)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+
+ return wpa_write_ftie(conf, sm->wpa_key_mgmt, sm->xxkey_len,
+ conf->r0_key_holder, conf->r0_key_holder_len,
+ NULL, NULL, buf, len, NULL, 0, 0);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+ u8 *fils_anonce, u8 *fils_snonce,
+ u8 *fils_kek, size_t *fils_kek_len)
+{
+ os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
+ os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
+ os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
+ *fils_kek_len = sm->PTK.kek_len;
+}
+
+
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *pmkid)
+{
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+ sm->pmkid_set = 1;
+}
+
+#endif /* CONFIG_FILS */
+
+
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
+{
+ if (sm)
+ sm->auth_alg = auth_alg;
+}
+
+
+#ifdef CONFIG_DPP2
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
+{
+ if (sm) {
+ wpabuf_clear_free(sm->dpp_z);
+ sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+ }
+}
+#endif /* CONFIG_DPP2 */
+
+
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+ u8 val)
+{
+ if (wpa_auth)
+ wpa_auth->conf.transition_disable = val;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ const u8 *anonce = sm->ANonce;
+ u8 anonce_buf[WPA_NONCE_LEN];
+
+ if (change_anonce) {
+ if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
+ return -1;
+ anonce = anonce_buf;
+ }
+
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake (TESTING)");
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+ anonce, NULL, 0, 0, 0);
+ return 0;
+}
+
+
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+ u8 *opos;
+ size_t gtk_len, kde_len;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_group *gsm = sm->group;
+ u8 *wpa_ie;
+ int wpa_ie_len, secure, gtkidx, encr = 0;
+ u8 hdr[2];
+
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+ GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
+ */
+
+ /* Use 0 RSC */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+ wpa_ie = sm->wpa_auth->wpa_ie;
+ wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+ if (sm->wpa == WPA_VERSION_WPA &&
+ (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+ /* WPA-only STA, remove RSN IE and possible MDIE */
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_RSNX)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ wpa_ie_len = wpa_ie[1] + 2;
+ }
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "sending 3/4 msg of 4-Way Handshake (TESTING)");
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ /* WPA2 send GTK in the 4-way handshake */
+ secure = 1;
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ gtkidx = gsm->GN;
+ _rsc = rsc;
+ encr = 1;
+ } else {
+ /* WPA does not include GTK in msg 3/4 */
+ secure = 0;
+ gtk = NULL;
+ gtk_len = 0;
+ _rsc = NULL;
+ if (sm->rx_eapol_key_secure) {
+ /*
+ * It looks like Windows 7 supplicant tries to use
+ * Secure bit in msg 2/4 after having reported Michael
+ * MIC failure and it then rejects the 4-way handshake
+ * if msg 3/4 does not set Secure bit. Work around this
+ * by setting the Secure bit here even in the case of
+ * WPA if the supplicant used it first.
+ */
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
+ secure = 1;
+ }
+ }
+
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+ if (sm->use_ext_key_id)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
+ if (gtk)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+ kde_len += 300; /* FTIE + 2 * TIE */
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ kde = os_malloc(kde_len);
+ if (!kde)
+ return -1;
+
+ pos = kde;
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ size_t elen;
+
+ elen = pos - kde;
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
+ os_free(kde);
+ return -1;
+ }
+ pos -= wpa_ie_len;
+ pos += elen;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ hdr[1] = 0;
+
+ if (sm->use_ext_key_id) {
+ hdr[0] = sm->keyidx_active & 0x01;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+ }
+
+ if (gtk) {
+ hdr[0] = gtkidx & 0x03;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ }
+ opos = pos;
+ pos = ieee80211w_kde_add(sm, pos);
+ if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
+ os_memset(opos, 0, 6); /* clear PN */
+ }
+ if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0) {
+ os_free(kde);
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+
+ if (sm->assoc_resp_ftie &&
+ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+ os_memcpy(pos, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]);
+ res = 2 + sm->assoc_resp_ftie[1];
+ } else {
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+ sm->xxkey_len,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, pos,
+ kde + kde_len - pos,
+ NULL, 0, 0);
+ }
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to insert FTIE into EAPOL-Key Key Data");
+ os_free(kde);
+ return -1;
+ }
+ pos += res;
+
+ /* TIE[ReassociationDeadline] (TU) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+ WPA_PUT_LE32(pos, conf->reassociation_deadline);
+ pos += 4;
+
+ /* TIE[KeyLifetime] (seconds) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+ pos += 4;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ (secure ? WPA_KEY_INFO_SECURE : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_KEY_TYPE,
+ _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+ bin_clear_free(kde, kde_len);
+ return 0;
+}
+
+
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_group *gsm = sm->group;
+ const u8 *kde;
+ u8 *kde_buf = NULL, *pos, hdr[2];
+ u8 *opos;
+ size_t kde_len;
+ u8 *gtk;
+
+ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ /* Use 0 RSC */
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "sending 1/2 msg of Group Key Handshake (TESTING)");
+
+ gtk = gsm->GTK[gsm->GN - 1];
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+ kde_buf = os_malloc(kde_len);
+ if (!kde_buf)
+ return -1;
+
+ kde = pos = kde_buf;
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gsm->GTK_len);
+ opos = pos;
+ pos = ieee80211w_kde_add(sm, pos);
+ if (pos - opos >=
+ 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
+ os_memset(opos, 0, 6); /* clear PN */
+ }
+ if (ocv_oci_add(sm, &pos,
+ conf->oci_freq_override_eapol_g1) < 0) {
+ os_free(kde_buf);
+ return -1;
+ }
+ kde_len = pos - kde;
+ } else {
+ kde = gtk;
+ kde_len = gsm->GTK_len;
+ }
+
+ sm->eapol_status_cb = cb;
+ sm->eapol_status_cb_ctx1 = ctx1;
+ sm->eapol_status_cb_ctx2 = ctx2;
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_SECURE |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK |
+ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+ rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+ bin_clear_free(kde_buf, kde_len);
+ return 0;
+}
+
+
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth)
+ return -1;
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+}
+
+
+int wpa_auth_rekey_ptk(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm)
+{
+ if (!wpa_auth || !sm)
+ return -1;
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+ wpa_request_new_ptk(sm);
+ wpa_sm_step(sm);
+ return 0;
+}
+
+
+void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val)
+{
+ if (wpa_auth)
+ wpa_auth->conf.ft_rsnxe_used = val;
+}
+
+
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+ enum wpa_auth_ocv_override_frame frame,
+ unsigned int freq)
+{
+ if (!wpa_auth)
+ return;
+ switch (frame) {
+ case WPA_AUTH_OCV_OVERRIDE_EAPOL_M3:
+ wpa_auth->conf.oci_freq_override_eapol_m3 = freq;
+ break;
+ case WPA_AUTH_OCV_OVERRIDE_EAPOL_G1:
+ wpa_auth->conf.oci_freq_override_eapol_g1 = freq;
+ break;
+ case WPA_AUTH_OCV_OVERRIDE_FT_ASSOC:
+ wpa_auth->conf.oci_freq_override_ft_assoc = freq;
+ break;
+ case WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC:
+ wpa_auth->conf.oci_freq_override_fils_assoc = freq;
+ break;
+ }
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success)
+{
+ if (!sm->waiting_radius_psk) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore RADIUS PSK response for " MACSTR
+ " that did not wait one",
+ MAC2STR(sm->addr));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS PSK response for " MACSTR " (%s)",
+ MAC2STR(sm->addr), success ? "success" : "fail");
+ sm->waiting_radius_psk = 0;
+
+ if (success) {
+ /* Try to process the EAPOL-Key msg 2/4 again */
+ sm->EAPOLKeyReceived = true;
+ } else {
+ sm->Disconnect = true;
+ }
+
+ eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
+
+
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ u8 mld_assoc_link_id, struct mld_info *info)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct wpa_auth_ml_rsn_info ml_rsn_info;
+ unsigned int link_id, i;
+
+ if (!info)
+ return;
+
+ os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+ "MLD: Initialization");
+
+ os_memcpy(sm->own_mld_addr, mld_addr, ETH_ALEN);
+ os_memcpy(sm->peer_mld_addr, info->common_info.mld_addr, ETH_ALEN);
+
+ sm->mld_assoc_link_id = mld_assoc_link_id;
+
+ os_memset(&ml_rsn_info, 0, sizeof(ml_rsn_info));
+
+ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ struct mld_link_info *link = &info->links[link_id];
+ struct mld_link *sm_link = &sm->mld_links[link_id];
+
+ sm_link->valid = link->valid;
+ if (!link->valid)
+ continue;
+
+ os_memcpy(sm_link->peer_addr, link->peer_addr, ETH_ALEN);
+ os_memcpy(sm_link->own_addr, link->local_addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: id=%u, addr=" MACSTR " peer=" MACSTR,
+ link_id,
+ MAC2STR(sm_link->own_addr),
+ MAC2STR(sm_link->peer_addr));
+
+ if (link_id != mld_assoc_link_id)
+ sm->n_mld_affiliated_links++;
+
+ ml_rsn_info.links[i++].link_id = link_id;
+ }
+
+ ml_rsn_info.n_mld_links = i;
+
+ wpa_auth_get_ml_rsn_info(sm->wpa_auth, &ml_rsn_info);
+
+ for (i = 0; i < ml_rsn_info.n_mld_links; i++) {
+ struct mld_link *sm_link;
+ const u8 *rsn_ies;
+ u8 rsn_ies_len;
+
+ sm_link = &sm->mld_links[ml_rsn_info.links[i].link_id];
+ rsn_ies = ml_rsn_info.links[i].rsn_ies;
+ rsn_ies_len = ml_rsn_info.links[i].rsn_ies_len;
+
+ /* This should not really happen */
+ if (!rsn_ies || rsn_ies_len < 2 || rsn_ies[0] != WLAN_EID_RSN ||
+ rsn_ies[1] + 2 > rsn_ies_len) {
+ wpa_printf(MSG_INFO, "WPA_AUTH: MLD: Invalid RSNE");
+ continue;
+ }
+
+ sm_link->rsne = rsn_ies;
+ sm_link->rsne_len = rsn_ies[1] + 2;
+
+ if (rsn_ies[1] + 2UL + 2UL < rsn_ies_len &&
+ rsn_ies[rsn_ies[1] + 2] == WLAN_EID_RSNX) {
+ sm_link->rsnxe = rsn_ies + 2 + rsn_ies[1];
+ sm_link->rsnxe_len = sm_link->rsnxe[1] + 2;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.h
new file mode 100644
index 0000000..c076d71
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.h
@@ -0,0 +1,643 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_H
+#define WPA_AUTH_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+
+struct vlan_description;
+struct mld_info;
+
+#define MAX_OWN_IE_OVERRIDE 256
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
+ */
+struct ft_rrb_frame {
+ u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+ u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
+ le16 action_length; /* little endian length of action_frame */
+ u8 ap_address[ETH_ALEN];
+ /*
+ * Followed by action_length bytes of FT Action frame (from Category
+ * field to the end of Action Frame body.
+ */
+} STRUCT_PACKED;
+
+#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
+
+#define FT_PACKET_REQUEST 0
+#define FT_PACKET_RESPONSE 1
+
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
+ * use OUI Extended EtherType as the encapsulating format. */
+#define FT_PACKET_R0KH_R1KH_PULL 0x01
+#define FT_PACKET_R0KH_R1KH_RESP 0x02
+#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
+
+/* packet layout
+ * IEEE 802 extended OUI ethertype frame header
+ * u16 authlen (little endian)
+ * multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
+ * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
+ * blocksize length)
+ *
+ * AES-SIV AAD;
+ * source MAC address (6)
+ * authenticated-only TLVs (authlen)
+ * subtype (1; FT_PACKET_*)
+ */
+
+#define FT_RRB_NONCE_LEN 16
+
+#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */
+
+#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */
+#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0 8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1 10 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE 11 /* le16 */
+#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */
+
+#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
+#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */
+
+#define FT_RRB_IDENTITY 15
+#define FT_RRB_RADIUS_CUI 16
+#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */
+
+struct ft_rrb_tlv {
+ le16 type;
+ le16 len;
+ /* followed by data of length len */
+} STRUCT_PACKED;
+
+struct ft_rrb_seq {
+ le32 dom;
+ le32 seq;
+ le32 ts;
+} STRUCT_PACKED;
+
+/* session TLVs:
+ * required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
+ * SESSION_TIMEOUT
+ *
+ * pull frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: S1KH_ID
+ * optional: session TLVs
+ *
+ * push frame TLVs:
+ * auth:
+ * required: SEQ, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ * auth:
+ * required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* per STA state machine data */
+
+struct wpa_authenticator;
+struct wpa_state_machine;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+struct ft_remote_seq;
+struct wpa_channel_info;
+
+
+struct ft_remote_r0kh {
+ struct ft_remote_r0kh *next;
+ u8 addr[ETH_ALEN];
+ u8 id[FT_R0KH_ID_MAX_LEN];
+ size_t id_len;
+ u8 key[32];
+ struct ft_remote_seq *seq;
+};
+
+
+struct ft_remote_r1kh {
+ struct ft_remote_r1kh *next;
+ u8 addr[ETH_ALEN];
+ u8 id[FT_R1KH_ID_LEN];
+ u8 key[32];
+ struct ft_remote_seq *seq;
+};
+
+
+struct wpa_auth_config {
+ void *msg_ctx;
+ int wpa;
+ int extended_key_id;
+ int wpa_key_mgmt;
+ int wpa_pairwise;
+ int wpa_group;
+ int wpa_group_rekey;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
+ int wpa_deny_ptk0_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
+ int wpa_disable_eapol_key_retries;
+ int rsn_pairwise;
+ int rsn_preauth;
+ int eapol_version;
+ int wmm_enabled;
+ int wmm_uapsd;
+ int disable_pmksa_caching;
+ int okc;
+ int tx_status;
+ enum mfp_options ieee80211w;
+ int beacon_prot;
+ int group_mgmt_cipher;
+ int sae_require_mfp;
+#ifdef CONFIG_OCV
+ int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211R_AP
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
+ size_t r0_key_holder_len;
+ u8 r1_key_holder[FT_R1KH_ID_LEN];
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+ int rkh_pos_timeout;
+ int rkh_neg_timeout;
+ int rkh_pull_timeout; /* ms */
+ int rkh_pull_retries;
+ int r1_max_key_lifetime;
+ u32 reassociation_deadline;
+ struct ft_remote_r0kh **r0kh_list;
+ struct ft_remote_r1kh **r1kh_list;
+ int pmk_r1_push;
+ int ft_over_ds;
+ int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
+ int disable_gtk;
+ int ap_mlme;
+#ifdef CONFIG_TESTING_OPTIONS
+ double corrupt_gtk_rekey_mic_probability;
+ u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
+ size_t own_ie_override_len;
+ u8 rsne_override_eapol[MAX_OWN_IE_OVERRIDE];
+ size_t rsne_override_eapol_len;
+ u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE];
+ size_t rsnxe_override_eapol_len;
+ u8 rsne_override_ft[MAX_OWN_IE_OVERRIDE];
+ size_t rsne_override_ft_len;
+ u8 rsnxe_override_ft[MAX_OWN_IE_OVERRIDE];
+ size_t rsnxe_override_ft_len;
+ u8 gtk_rsc_override[WPA_KEY_RSC_LEN];
+ u8 igtk_rsc_override[WPA_KEY_RSC_LEN];
+ unsigned int rsne_override_eapol_set:1;
+ unsigned int rsnxe_override_eapol_set:1;
+ unsigned int rsne_override_ft_set:1;
+ unsigned int rsnxe_override_ft_set:1;
+ unsigned int gtk_rsc_override_set:1;
+ unsigned int igtk_rsc_override_set:1;
+ int ft_rsnxe_used;
+ bool delay_eapol_tx;
+#endif /* CONFIG_TESTING_OPTIONS */
+ unsigned int oci_freq_override_eapol_m3;
+ unsigned int oci_freq_override_eapol_g1;
+ unsigned int oci_freq_override_ft_assoc;
+ unsigned int oci_freq_override_fils_assoc;
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+ unsigned int fils_cache_id_set:1;
+ u8 fils_cache_id[FILS_CACHE_ID_LEN];
+#endif /* CONFIG_FILS */
+ enum sae_pwe sae_pwe;
+ bool sae_pk;
+
+ unsigned int secure_ltf:1;
+ unsigned int secure_rtt:1;
+ unsigned int prot_range_neg:1;
+
+ int owe_ptk_workaround;
+ u8 transition_disable;
+#ifdef CONFIG_DPP2
+ int dpp_pfs;
+#endif /* CONFIG_DPP2 */
+
+ /*
+ * If set Key Derivation Key should be derived as part of PMK to
+ * PTK derivation regardless of advertised capabilities.
+ */
+ bool force_kdk_derivation;
+
+ bool radius_psk;
+};
+
+typedef enum {
+ LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
+} logger_level;
+
+typedef enum {
+ WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
+ WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
+ WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
+} wpa_eapol_variable;
+
+struct wpa_auth_ml_rsn_info {
+ unsigned int n_mld_links;
+
+ struct wpa_auth_ml_link_rsn_info {
+ unsigned int link_id;
+ const u8 *rsn_ies;
+ size_t rsn_ies_len;
+ } links[MAX_NUM_MLD_LINKS];
+};
+
+struct wpa_auth_ml_key_info {
+ unsigned int n_mld_links;
+ bool mgmt_frame_prot;
+ bool beacon_prot;
+
+ struct wpa_auth_ml_link_key_info {
+ u8 link_id;
+
+ u8 gtkidx;
+ u8 gtk_len;
+ u8 pn[6];
+ const u8 *gtk;
+
+ u8 igtkidx;
+ u8 igtk_len;
+ const u8 *igtk;
+ u8 ipn[6];
+
+ u8 bigtkidx;
+ const u8 *bigtk;
+ u8 bipn[6];
+ } links[MAX_NUM_MLD_LINKS];
+};
+
+struct wpa_auth_callbacks {
+ void (*logger)(void *ctx, const u8 *addr, logger_level level,
+ const char *txt);
+ void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
+ int (*mic_failure_report)(void *ctx, const u8 *addr);
+ void (*psk_failure_report)(void *ctx, const u8 *addr);
+ void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
+ int value);
+ int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
+ const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk, size_t *psk_len,
+ int *vlan_id);
+ int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
+ int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
+ const u8 *addr, int idx, u8 *key, size_t key_len,
+ enum key_flag key_flag);
+ int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
+ int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt);
+ int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+ void *ctx), void *cb_ctx);
+ int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
+ void *ctx), void *cb_ctx);
+ int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
+ size_t data_len);
+ int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
+ size_t data_len);
+ int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
+ int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
+ int (*get_sta_tx_params)(void *ctx, const u8 *addr,
+ int ap_max_chanwidth, int ap_seg1_idx,
+ int *bandwidth, int *seg1_idx);
+ void (*store_ptksa)(void *ctx, const u8 *addr, int cipher,
+ u32 life_time, const struct wpa_ptk *ptk);
+ void (*clear_ptksa)(void *ctx, const u8 *addr, int cipher);
+ void (*request_radius_psk)(void *ctx, const u8 *addr, int key_mgmt,
+ const u8 *anonce,
+ const u8 *eapol, size_t eapol_len);
+#ifdef CONFIG_IEEE80211R_AP
+ struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+ int (*add_sta_ft)(void *ctx, const u8 *sta_addr);
+ int (*set_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*get_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*set_identity)(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len);
+ size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len);
+ size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
+ int session_timeout);
+ int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
+
+ int (*send_ft_action)(void *ctx, const u8 *dst,
+ const u8 *data, size_t data_len);
+ int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+ size_t tspec_ielen);
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_MESH
+ int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_PASN
+ int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len);
+#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+ int (*get_ml_rsn_info)(void *ctx, struct wpa_auth_ml_rsn_info *info);
+ int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info);
+#endif /* CONFIG_IEEE80211BE */
+};
+
+struct wpa_authenticator * wpa_init(const u8 *addr,
+ struct wpa_auth_config *conf,
+ const struct wpa_auth_callbacks *cb,
+ void *cb_ctx);
+int wpa_init_keys(struct wpa_authenticator *wpa_auth);
+void wpa_deinit(struct wpa_authenticator *wpa_auth);
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_config *conf);
+
+enum wpa_validate_result {
+ WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+ WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
+ WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
+ WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID,
+ WPA_DENIED_OTHER_REASON
+};
+
+enum wpa_validate_result
+wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int freq,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsnxe, size_t rsnxe_len,
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len);
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len);
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr);
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm);
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ u8 *data, size_t data_len);
+enum wpa_event {
+ WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+ WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
+};
+void wpa_remove_ptk(struct wpa_state_machine *sm);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
+void wpa_auth_sm_notify(struct wpa_state_machine *sm);
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
+ size_t *len);
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+ unsigned int pmk_len,
+ int session_timeout, struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+ const u8 *pmk, size_t len, const u8 *sta_addr,
+ int session_timeout,
+ struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int akmp);
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp);
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp, const u8 *dpp_pkhash);
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr);
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+ size_t len);
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ size_t pmk_len, int akmp,
+ const u8 *pmkid, int expiration);
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 *pmkid);
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+ struct wpa_state_machine *sm,
+ struct wpa_authenticator *wpa_auth,
+ u8 *pmkid, u8 *pmk);
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack);
+
+#ifdef CONFIG_IEEE80211R_AP
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+ size_t max_len, int auth_alg,
+ const u8 *req_ies, size_t req_ies_len,
+ int omit_rsnxe);
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+ u16 auth_transaction, const u8 *ies, size_t ies_len,
+ void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 resp,
+ const u8 *ies, size_t ies_len),
+ void *ctx);
+int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len);
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *data, size_t data_len);
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+ size_t data_len);
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
+int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r1_name,
+ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui, size_t *radius_cui_len,
+ int *session_timeout);
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+ struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *snonce, const u8 *anonce,
+ const u8 *dhss, size_t dhss_len,
+ struct wpabuf *g_sta, struct wpabuf *g_ap);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+ const struct ieee80211_mgmt *mgmt, size_t frame_len,
+ u8 *pos, size_t left);
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+ size_t current_len, size_t max_len,
+ const struct wpabuf *hlp);
+int fils_set_tk(struct wpa_state_machine *sm);
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
+ const u8 *fils_session,
+ struct wpabuf *fils_hlp_resp);
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *fils_session);
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len);
+
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ u8 *buf, size_t len);
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+ u8 *fils_anonce, u8 *fils_snonce,
+ u8 *fils_kek, size_t *fils_kek_len);
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *pmkid);
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len,
+ const u8 *req_ies, size_t req_ies_len);
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len,
+ const u8 *req_ies, size_t req_ies_len);
+bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
+ u8 *fd_rsn_info);
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+ u8 val);
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_rekey_ptk(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm);
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
+int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
+ const u8 *data, size_t data_len,
+ int encrypt);
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
+void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val);
+
+enum wpa_auth_ocv_override_frame {
+ WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+ WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+ WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+ WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC,
+};
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+ enum wpa_auth_ocv_override_frame frame,
+ unsigned int freq);
+
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
+
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+ u8 mld_assoc_link_id, struct mld_info *info);
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_rsn_info *info);
+void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+ struct wpa_auth_ml_link_key_info *info,
+ bool mgmt_frame_prot, bool beacon_prot);
+
+#endif /* WPA_AUTH_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ft.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ft.c
new file mode 100644
index 0000000..2402ad9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ft.c
@@ -0,0 +1,4953 @@
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "crypto/random.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wmm.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "pmksa_cache_auth.h"
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+const unsigned int ftRRBseqTimeout = 10;
+const unsigned int ftRRBmaxQueueLen = 100;
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len);
+static void ft_finish_pull(struct wpa_state_machine *sm);
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
+
+struct tlv_list {
+ u16 type;
+ size_t len;
+ const u8 *data;
+};
+
+
+/**
+ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @enc: Pointer to encrypted TLVs
+ * @enc_len: Length of encrypted TLVs in octets
+ * @auth: Pointer to authenticated TLVs
+ * @auth_len: Length of authenticated TLVs in octets
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
+ * needs to be freed by the caller if not NULL;
+ * will only be returned on success
+ * @plain_len: Pointer to return the length of the allocated plaintext buffer
+ * in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, const size_t auth_len,
+ const u8 *src_addr, u8 type,
+ u8 **plain, size_t *plain_size)
+{
+ const u8 *ad[3] = { src_addr, auth, &type };
+ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+ MAC2STR(src_addr), type);
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
+
+ if (!key) { /* skip decryption */
+ *plain = os_memdup(enc, enc_len);
+ if (enc_len > 0 && !*plain)
+ goto err;
+
+ *plain_size = enc_len;
+
+ return 0;
+ }
+
+ *plain = NULL;
+
+ /* SIV overhead */
+ if (enc_len < AES_BLOCK_SIZE)
+ goto err;
+
+ *plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
+ if (!*plain)
+ goto err;
+
+ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+ *plain) < 0) {
+ if (enc_len < AES_BLOCK_SIZE + 2)
+ goto err;
+
+ /* Try to work around Ethernet devices that add extra
+ * two octet padding even if the frame is longer than
+ * the minimum Ethernet frame. */
+ enc_len -= 2;
+ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+ *plain) < 0)
+ goto err;
+ }
+
+ *plain_size = enc_len - AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
+ *plain, *plain_size);
+ return 0;
+err:
+ os_free(*plain);
+ *plain = NULL;
+ *plain_size = 0;
+
+ wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
+
+ return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
+ u16 type, size_t *tlv_len, const u8 **tlv_data)
+{
+ const struct ft_rrb_tlv *f;
+ size_t left;
+ le16 type16;
+ size_t len;
+
+ left = plain_len;
+ type16 = host_to_le16(type);
+
+ while (left >= sizeof(*f)) {
+ f = (const struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+ len = le_to_host16(f->len);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ break;
+ }
+
+ if (f->type == type16) {
+ *tlv_len = len;
+ *tlv_data = plain;
+ return 0;
+ }
+
+ left -= len;
+ plain += len;
+ }
+
+ return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+ const struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+
+ left = plain_len;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+ while (left >= sizeof(*f)) {
+ f = (const struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+ len = le_to_host16(f->len);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+ le_to_host16(f->type), len);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB message truncated: left %zu bytes, need %zu",
+ left, len);
+ break;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+ left -= len;
+ plain += len;
+ }
+
+ if (left > 0)
+ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static int cmp_int(const void *a, const void *b)
+{
+ int x, y;
+
+ x = *((int *) a);
+ y = *((int *) b);
+ return x - y;
+}
+
+
+static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
+ struct vlan_description *vlan)
+{
+ struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+ int taggedidx;
+ int vlan_id;
+ int type;
+
+ left = plain_len;
+ taggedidx = 0;
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ while (left >= sizeof(*f)) {
+ f = (struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+
+ len = le_to_host16(f->len);
+ type = le_to_host16(f->type);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ return -1;
+ }
+
+ if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
+ goto skip;
+
+ if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_UNTAGGED invalid length");
+ return -1;
+ }
+
+ if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_TAGGED invalid length");
+ return -1;
+ }
+
+ while (len >= sizeof(le16)) {
+ vlan_id = WPA_GET_LE16(plain);
+ plain += sizeof(le16);
+ left -= sizeof(le16);
+ len -= sizeof(le16);
+
+ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN ID invalid %d",
+ vlan_id);
+ continue;
+ }
+
+ if (type == FT_RRB_VLAN_UNTAGGED)
+ vlan->untagged = vlan_id;
+
+ if (type == FT_RRB_VLAN_TAGGED &&
+ taggedidx < MAX_NUM_TAGGED_VLAN) {
+ vlan->tagged[taggedidx] = vlan_id;
+ taggedidx++;
+ } else if (type == FT_RRB_VLAN_TAGGED) {
+ wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
+ }
+ }
+
+ skip:
+ left -= len;
+ plain += len;
+ }
+
+ if (taggedidx)
+ qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
+
+ vlan->notempty = vlan->untagged || vlan->tagged[0];
+
+ return 0;
+}
+
+
+static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!tlvs)
+ return 0;
+
+ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += tlvs[i].len;
+ }
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+ u8 *endpos)
+{
+ int i;
+ size_t tlv_len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos;
+
+ if (!tlvs)
+ return 0;
+
+ tlv_len = 0;
+ pos = start;
+ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+ if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
+ return tlv_len;
+ tlv_len += sizeof(*hdr);
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(tlvs[i].type);
+ hdr->len = host_to_le16(tlvs[i].len);
+ pos = start + tlv_len;
+
+ if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
+ return tlv_len;
+ if (tlvs[i].len == 0)
+ continue;
+ tlv_len += tlvs[i].len;
+ os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+ pos = start + tlv_len;
+ }
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ if (vlan->untagged) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += sizeof(le16);
+ }
+ if (vlan->tagged[0])
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
+ tlv_len += sizeof(le16);
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
+ u8 *start, u8 *endpos)
+{
+ size_t tlv_len;
+ int i, len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos = start;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ tlv_len = 0;
+ if (vlan->untagged) {
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
+ hdr->len = host_to_le16(sizeof(le16));
+ pos = start + tlv_len;
+
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ WPA_PUT_LE16(pos, vlan->untagged);
+ pos = start + tlv_len;
+ }
+
+ if (!vlan->tagged[0])
+ return tlv_len;
+
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
+ len = 0; /* len is computed below */
+ pos = start + tlv_len;
+
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ break;
+ len += sizeof(le16);
+ WPA_PUT_LE16(pos, vlan->tagged[i]);
+ pos = start + tlv_len;
+ }
+
+ hdr->len = host_to_le16(len);
+
+ return tlv_len;
+}
+
+
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+ const struct tlv_list *tlvs2,
+ const struct vlan_description *vlan,
+ u8 **plain, size_t *plain_len)
+{
+ u8 *pos, *endpos;
+ size_t tlv_len;
+
+ tlv_len = wpa_ft_tlv_len(tlvs1);
+ tlv_len += wpa_ft_tlv_len(tlvs2);
+ tlv_len += wpa_ft_vlan_len(vlan);
+
+ *plain_len = tlv_len;
+ *plain = os_zalloc(tlv_len);
+ if (!*plain) {
+ wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+ goto err;
+ }
+
+ pos = *plain;
+ endpos = *plain + tlv_len;
+ pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+ pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+ pos += wpa_ft_vlan_lin(vlan, pos, endpos);
+
+ /* validity check */
+ if (pos != endpos) {
+ wpa_printf(MSG_ERROR, "FT: Length error building RRB");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ os_free(*plain);
+ *plain = NULL;
+ *plain_len = 0;
+ return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+ const u8 *plain, const size_t plain_len,
+ const u8 *auth, const size_t auth_len,
+ const u8 *src_addr, u8 type, u8 *enc)
+{
+ const u8 *ad[3] = { src_addr, auth, &type };
+ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+ MAC2STR(src_addr), type);
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
+ plain, plain_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
+
+ if (!key) {
+ /* encryption not needed, return plaintext as packet */
+ os_memcpy(enc, plain, plain_len);
+ } else if (aes_siv_encrypt(key, key_len, plain, plain_len,
+ 3, ad, ad_len, enc) < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
+ enc, plain_len + AES_BLOCK_SIZE);
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @tlvs_enc0: First set of to-be-encrypted TLVs
+ * @tlvs_enc1: Second set of to-be-encrypted TLVs
+ * @tlvs_auth: Set of to-be-authenticated TLVs
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @packet Pointer to return the pointer to the allocated packet buffer;
+ * needs to be freed by the caller if not null;
+ * will only be returned on success
+ * @packet_len: Pointer to return the length of the allocated buffer in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+ const struct tlv_list *tlvs_enc0,
+ const struct tlv_list *tlvs_enc1,
+ const struct tlv_list *tlvs_auth,
+ const struct vlan_description *vlan,
+ const u8 *src_addr, u8 type,
+ u8 **packet, size_t *packet_len)
+{
+ u8 *plain = NULL, *auth = NULL, *pos, *tmp;
+ size_t plain_len = 0, auth_len = 0;
+ int ret = -1;
+ size_t pad_len = 0;
+
+ *packet = NULL;
+ if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
+ goto out;
+
+ if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
+ goto out;
+
+ *packet_len = sizeof(u16) + auth_len + plain_len;
+ if (key)
+ *packet_len += AES_BLOCK_SIZE;
+#define RRB_MIN_MSG_LEN 64
+ if (*packet_len < RRB_MIN_MSG_LEN) {
+ pad_len = RRB_MIN_MSG_LEN - *packet_len;
+ if (pad_len < sizeof(struct ft_rrb_tlv))
+ pad_len = sizeof(struct ft_rrb_tlv);
+ wpa_printf(MSG_DEBUG,
+ "FT: Pad message to minimum Ethernet frame length (%d --> %d)",
+ (int) *packet_len, (int) (*packet_len + pad_len));
+ *packet_len += pad_len;
+ tmp = os_realloc(auth, auth_len + pad_len);
+ if (!tmp)
+ goto out;
+ auth = tmp;
+ pos = auth + auth_len;
+ WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
+ pos += 2;
+ WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
+ pos += 2;
+ os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
+ auth_len += pad_len;
+
+ }
+ *packet = os_zalloc(*packet_len);
+ if (!*packet)
+ goto out;
+
+ pos = *packet;
+ WPA_PUT_LE16(pos, auth_len);
+ pos += 2;
+ os_memcpy(pos, auth, auth_len);
+ pos += auth_len;
+ if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+ auth_len, src_addr, type, pos) < 0)
+ goto out;
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
+
+ ret = 0;
+
+out:
+ bin_clear_free(plain, plain_len);
+ os_free(auth);
+
+ if (ret) {
+ wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+ os_free(*packet);
+ *packet = NULL;
+ *packet_len = 0;
+ }
+
+ return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+ &f_##field##_len, &f_##field) < 0 || \
+ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+ wpa_printf(MSG_INFO, "FT: Missing required " #field \
+ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+ wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+ goto out; \
+ } \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+ RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+ RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+ &f_##field##_len, &f_##field) < 0 || \
+ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+ wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
+ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+ f_##field##_len = 0; \
+ f_##field = NULL; \
+ } \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+ RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+ RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
+
+static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
+ const u8 *data, size_t data_len)
+{
+ if (wpa_auth->cb->send_ether == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+ return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
+ data, data_len);
+}
+
+
+static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
+ const u8 *dst, u8 oui_suffix,
+ const u8 *data, size_t data_len)
+{
+ if (!wpa_auth->cb->send_oui)
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
+ oui_suffix, MAC2STR(dst), (unsigned int) data_len);
+ return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
+ data_len);
+}
+
+
+static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
+ const u8 *dst, const u8 *data, size_t data_len)
+{
+ if (wpa_auth->cb->send_ft_action == NULL)
+ return -1;
+ return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
+ data, data_len);
+}
+
+
+static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
+{
+ if (wpa_auth->cb->get_psk == NULL)
+ return NULL;
+ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+ prev_psk, NULL, NULL);
+}
+
+
+static struct wpa_state_machine *
+wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+ if (wpa_auth->cb->add_sta == NULL)
+ return NULL;
+ return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
+}
+
+
+static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->set_vlan)
+ return -1;
+ return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->get_vlan)
+ return -1;
+ return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ if (!wpa_auth->cb->set_identity)
+ return -1;
+ return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
+ identity_len);
+}
+
+
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_identity)
+ return 0;
+ return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static int
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ if (!wpa_auth->cb->set_radius_cui)
+ return -1;
+ return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
+ radius_cui, radius_cui_len);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_radius_cui)
+ return 0;
+ return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static void
+wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, int session_timeout)
+{
+ if (!wpa_auth->cb->set_session_timeout)
+ return;
+ wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
+ session_timeout);
+}
+
+
+static int
+wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+{
+ if (!wpa_auth->cb->get_session_timeout)
+ return 0;
+ return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
+}
+
+
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+{
+ if (wpa_auth->cb->add_tspec == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+ return -1;
+ }
+ return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
+ tspec_ielen);
+}
+
+
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_channel_info *ci)
+{
+ if (!wpa_auth->cb->channel_info)
+ return -1;
+ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ u8 *pos = buf;
+ u8 capab;
+ if (len < 2 + sizeof(struct rsn_mdie))
+ return -1;
+
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
+ os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+ pos += MOBILITY_DOMAIN_ID_LEN;
+ capab = 0;
+ if (conf->ft_over_ds)
+ capab |= RSN_FT_CAPAB_FT_OVER_DS;
+ *pos++ = capab;
+
+ return pos - buf;
+}
+
+
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *anonce, const u8 *snonce,
+ u8 *buf, size_t len, const u8 *subelem,
+ size_t subelem_len, int rsnxe_used)
+{
+ u8 *pos = buf, *ielen;
+ size_t hdrlen;
+ u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA256_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie);
+ else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie_sha384);
+ else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN)
+ hdrlen = sizeof(struct rsn_ftie_sha512);
+ else if (wpa_key_mgmt_sha384(key_mgmt))
+ hdrlen = sizeof(struct rsn_ftie_sha384);
+ else
+ hdrlen = sizeof(struct rsn_ftie);
+
+ if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+ subelem_len)
+ return -1;
+
+ *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+ ielen = pos++;
+
+ if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(key_mgmt)) {
+ struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+ WPA_PUT_LE16(hdr->mic_control, mic_control);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ }
+
+ /* Optional Parameters */
+ *pos++ = FTIE_SUBELEM_R1KH_ID;
+ *pos++ = FT_R1KH_ID_LEN;
+ os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
+ pos += FT_R1KH_ID_LEN;
+
+ if (r0kh_id) {
+ *pos++ = FTIE_SUBELEM_R0KH_ID;
+ *pos++ = r0kh_id_len;
+ os_memcpy(pos, r0kh_id, r0kh_id_len);
+ pos += r0kh_id_len;
+ }
+
+ if (subelem) {
+ os_memcpy(pos, subelem, subelem_len);
+ pos += subelem_len;
+ }
+
+ *ielen = pos - buf - 2;
+
+ return pos - buf;
+}
+
+
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+ struct dl_list list;
+
+ u8 nonce[FT_RRB_NONCE_LEN];
+ struct os_reltime nonce_ts;
+
+ u8 src_addr[ETH_ALEN];
+ u8 *enc;
+ size_t enc_len;
+ u8 *auth;
+ size_t auth_len;
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+ dl_list_del(&item->list);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item->auth);
+ os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, int cb)
+{
+ struct ft_remote_item *item, *n;
+
+ dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+ struct ft_remote_item, list) {
+ if (cb && item->cb)
+ item->cb(wpa_auth, item->src_addr, item->enc,
+ item->enc_len, item->auth, item->auth_len, 1);
+ wpa_ft_rrb_seq_free(item);
+ }
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ft_remote_item *item = timeout_ctx;
+
+ wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ struct ft_remote_item *item = NULL;
+ u8 *packet = NULL;
+ size_t packet_len;
+ struct tlv_list seq_req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = NULL /* to be filled: item->nonce */ },
+ { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+ .data = f_r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+ wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+ goto err;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Send sequence number request from " MACSTR
+ " to " MACSTR,
+ MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
+ item = os_zalloc(sizeof(*item));
+ if (!item)
+ goto err;
+
+ os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+ item->cb = cb;
+
+ if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+ goto err;
+ }
+
+ if (os_get_reltime(&item->nonce_ts) < 0)
+ goto err;
+
+ if (enc && enc_len > 0) {
+ item->enc = os_memdup(enc, enc_len);
+ item->enc_len = enc_len;
+ if (!item->enc)
+ goto err;
+ }
+
+ if (auth && auth_len > 0) {
+ item->auth = os_memdup(auth, auth_len);
+ item->auth_len = auth_len;
+ if (!item->auth)
+ goto err;
+ }
+
+ eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+ wpa_auth, item);
+
+ seq_req_auth[0].data = item->nonce;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ &packet, &packet_len) < 0) {
+ item = NULL; /* some other seq resp might still accept this */
+ goto err;
+ }
+
+ dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ packet, packet_len);
+
+ os_free(packet);
+
+ return 0;
+err:
+ wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+ if (item) {
+ os_free(item->auth);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item);
+ }
+
+ return -1;
+}
+
+
+#define FT_RRB_SEQ_OK 0
+#define FT_RRB_SEQ_DROP 1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, int no_defer)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, rkh_off;
+ struct os_reltime now;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ if (rkh_seq->rx.num_last == 0) {
+ /* first packet from remote */
+ goto defer;
+ }
+
+ if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+ /* remote might have rebooted */
+ goto defer;
+ }
+
+ if (os_get_reltime(&now) == 0) {
+ u32 msg_ts_now_remote, msg_ts_off;
+ struct os_reltime now_remote;
+
+ os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+ msg_ts_now_remote = now_remote.sec;
+ msg_ts_off = le_to_host32(msg_both->ts) -
+ (msg_ts_now_remote - ftRRBseqTimeout);
+ if (msg_ts_off > 2 * ftRRBseqTimeout)
+ goto defer;
+ }
+
+ msg_seq = le_to_host32(msg_both->seq);
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ msg_off = msg_seq - rkh_off;
+ if (msg_off > 0xC0000000)
+ goto out; /* too old message, drop it */
+
+ if (msg_off <= 0x40000000) {
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ if (rkh_seq->rx.last[i] == msg_seq)
+ goto out; /* duplicate message, drop it */
+ }
+
+ return FT_RRB_SEQ_OK;
+ }
+
+defer:
+ if (no_defer)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+ MACSTR, msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DEFER;
+out:
+ wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+ msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, min_off, rkh_off;
+ int minidx = 0;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_seq = le_to_host32(msg_both->seq);
+
+ if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+ rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+ rkh_seq->rx.num_last++;
+ return;
+ }
+
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ msg_off = rkh_seq->rx.last[i] - rkh_off;
+ min_off = rkh_seq->rx.last[minidx] - rkh_off;
+ if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+ minidx = i;
+ }
+ rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+ rkh_seq->rx.offsetidx = minidx;
+
+ return;
+out:
+ /* RRB_GET_AUTH should never fail here as
+ * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+ wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+ struct ft_rrb_seq *f_seq)
+{
+ struct os_reltime now;
+
+ if (os_get_reltime(&now) < 0)
+ return -1;
+
+ if (!rkh_seq->tx.dom) {
+ if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+ sizeof(rkh_seq->tx.seq))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.seq = now.usec;
+ }
+ if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+ sizeof(rkh_seq->tx.dom))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.dom = now.usec;
+ }
+ rkh_seq->tx.dom |= 1;
+ }
+
+ f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+ f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+ f_seq->ts = host_to_le32(now.sec);
+
+ rkh_seq->tx.seq++;
+
+ return 0;
+}
+
+
+struct wpa_ft_pmk_r0_sa {
+ struct dl_list list;
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 spa[ETH_ALEN];
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+ struct vlan_description *vlan;
+ os_time_t expiration; /* 0 for no expiration */
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
+ int pmk_r1_pushed;
+};
+
+struct wpa_ft_pmk_r1_sa {
+ struct dl_list list;
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 spa[ETH_ALEN];
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+ struct vlan_description *vlan;
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
+};
+
+struct wpa_ft_pmk_cache {
+ struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
+ struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
+};
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
+{
+ if (!r0)
+ return;
+
+ dl_list_del(&r0->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+
+ os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
+ os_free(r0->vlan);
+ os_free(r0->identity);
+ os_free(r0->radius_cui);
+ os_free(r0);
+}
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
+ struct os_reltime now;
+ int expires_in;
+ int session_timeout;
+
+ os_get_reltime(&now);
+
+ if (!r0)
+ return;
+
+ expires_in = r0->expiration - now.sec;
+ session_timeout = r0->session_timeout - now.sec;
+ /* conditions to remove from cache:
+ * a) r0->expiration is set and hit
+ * -or-
+ * b) r0->session_timeout is set and hit
+ */
+ if ((!r0->expiration || expires_in > 0) &&
+ (!r0->session_timeout || session_timeout > 0)) {
+ wpa_printf(MSG_ERROR,
+ "FT: %s() called for non-expired entry %p",
+ __func__, r0);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->expiration && expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->session_timeout && session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ return;
+ }
+
+ wpa_ft_free_pmk_r0(r0);
+}
+
+
+static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
+{
+ if (!r1)
+ return;
+
+ dl_list_del(&r1->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
+
+ os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
+ os_free(r1->vlan);
+ os_free(r1->identity);
+ os_free(r1->radius_cui);
+ os_free(r1);
+}
+
+
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
+
+ wpa_ft_free_pmk_r1(r1);
+}
+
+
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
+{
+ struct wpa_ft_pmk_cache *cache;
+
+ cache = os_zalloc(sizeof(*cache));
+ if (cache) {
+ dl_list_init(&cache->pmk_r0);
+ dl_list_init(&cache->pmk_r1);
+ }
+
+ return cache;
+}
+
+
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
+{
+ struct wpa_ft_pmk_r0_sa *r0, *r0prev;
+ struct wpa_ft_pmk_r1_sa *r1, *r1prev;
+
+ dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
+ struct wpa_ft_pmk_r0_sa, list)
+ wpa_ft_free_pmk_r0(r0);
+
+ dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
+ struct wpa_ft_pmk_r1_sa, list)
+ wpa_ft_free_pmk_r1(r1);
+
+ os_free(cache);
+}
+
+
+static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r0,
+ size_t pmk_r0_len,
+ const u8 *pmk_r0_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
+
+ /* TODO: add limit on number of entries in cache */
+ os_get_reltime(&now);
+
+ r0 = os_zalloc(sizeof(*r0));
+ if (r0 == NULL)
+ return -1;
+
+ os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
+ r0->pmk_r0_len = pmk_r0_len;
+ os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+ os_memcpy(r0->spa, spa, ETH_ALEN);
+ r0->pairwise = pairwise;
+ if (expires_in > 0)
+ r0->expiration = now.sec + expires_in;
+ if (vlan && vlan->notempty) {
+ r0->vlan = os_zalloc(sizeof(*vlan));
+ if (!r0->vlan) {
+ bin_clear_free(r0, sizeof(*r0));
+ return -1;
+ }
+ *r0->vlan = *vlan;
+ }
+ if (identity) {
+ r0->identity = os_malloc(identity_len);
+ if (r0->identity) {
+ os_memcpy(r0->identity, identity, identity_len);
+ r0->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r0->radius_cui = os_malloc(radius_cui_len);
+ if (r0->radius_cui) {
+ os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+ r0->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r0->session_timeout = now.sec + session_timeout;
+
+ dl_list_add(&cache->pmk_r0, &r0->list);
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
+ r0, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+
+ return 0;
+}
+
+
+static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r0_name,
+ const struct wpa_ft_pmk_r0_sa **r0_out)
+{
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+ if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+ os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+ WPA_PMK_NAME_LEN) == 0) {
+ *r0_out = r0;
+ return 0;
+ }
+ }
+
+ *r0_out = NULL;
+ return -1;
+}
+
+
+static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r1,
+ size_t pmk_r1_len,
+ const u8 *pmk_r1_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
+ struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
+
+ /* TODO: limit on number of entries in cache */
+ os_get_reltime(&now);
+
+ if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
+ expires_in = max_expires_in;
+
+ r1 = os_zalloc(sizeof(*r1));
+ if (r1 == NULL)
+ return -1;
+
+ os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
+ r1->pmk_r1_len = pmk_r1_len;
+ os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+ os_memcpy(r1->spa, spa, ETH_ALEN);
+ r1->pairwise = pairwise;
+ if (vlan && vlan->notempty) {
+ r1->vlan = os_zalloc(sizeof(*vlan));
+ if (!r1->vlan) {
+ bin_clear_free(r1, sizeof(*r1));
+ return -1;
+ }
+ *r1->vlan = *vlan;
+ }
+ if (identity) {
+ r1->identity = os_malloc(identity_len);
+ if (r1->identity) {
+ os_memcpy(r1->identity, identity, identity_len);
+ r1->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r1->radius_cui = os_malloc(radius_cui_len);
+ if (r1->radius_cui) {
+ os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+ r1->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r1->session_timeout = now.sec + session_timeout;
+
+ dl_list_add(&cache->pmk_r1, &r1->list);
+
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
+ r1, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r1, r1, NULL);
+
+ return 0;
+}
+
+
+int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *spa, const u8 *pmk_r1_name,
+ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui, size_t *radius_cui_len,
+ int *session_timeout)
+{
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+
+ dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
+ if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+ os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
+ WPA_PMK_NAME_LEN) == 0) {
+ os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
+ *pmk_r1_len = r1->pmk_r1_len;
+ if (pairwise)
+ *pairwise = r1->pairwise;
+ if (vlan && r1->vlan)
+ *vlan = *r1->vlan;
+ if (vlan && !r1->vlan)
+ os_memset(vlan, 0, sizeof(*vlan));
+ if (identity && identity_len) {
+ *identity = r1->identity;
+ *identity_len = r1->identity_len;
+ }
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r1->radius_cui;
+ *radius_cui_len = r1->radius_cui_len;
+ }
+ if (session_timeout && r1->session_timeout > now.sec)
+ *session_timeout = r1->session_timeout -
+ now.sec;
+ else if (session_timeout && r1->session_timeout)
+ *session_timeout = 1;
+ else if (session_timeout)
+ *session_timeout = 0;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+ if (r0kh->seq)
+ return 0;
+
+ r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+ if (!r0kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r0kh->seq->rx.queue);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ struct ft_remote_r0kh **r0kh_out,
+ struct ft_remote_r0kh **r0kh_wildcard)
+{
+ struct ft_remote_r0kh *r0kh;
+
+ *r0kh_wildcard = NULL;
+ *r0kh_out = NULL;
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ for (; r0kh; r0kh = r0kh->next) {
+ if (r0kh->id_len == 1 && r0kh->id[0] == '*')
+ *r0kh_wildcard = r0kh;
+ if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+ os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
+ *r0kh_out = r0kh;
+ }
+
+ if (!*r0kh_out && !*r0kh_wildcard)
+ wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+ if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+ *r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+ if (r1kh->seq)
+ return 0;
+
+ r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+ if (!r1kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r1kh->seq->rx.queue);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r1kh_id,
+ struct ft_remote_r1kh **r1kh_out,
+ struct ft_remote_r1kh **r1kh_wildcard)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ *r1kh_wildcard = NULL;
+ *r1kh_out = NULL;
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ for (; r1kh; r1kh = r1kh->next) {
+ if (is_zero_ether_addr(r1kh->addr) &&
+ is_zero_ether_addr(r1kh->id))
+ *r1kh_wildcard = r1kh;
+ if (f_r1kh_id &&
+ os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
+ *r1kh_out = r1kh;
+ }
+
+ if (!*r1kh_out && !*r1kh_wildcard)
+ wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+ if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+ *r1kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+ if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+ os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+ f_r0kh_id_len) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r1kh_id)
+{
+ if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+ if (!wpa_auth->conf.r0kh_list)
+ return;
+
+ for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
+ if (r0kh == timeout_ctx)
+ break;
+ prev = r0kh;
+ }
+ if (!r0kh)
+ return;
+ if (prev)
+ prev->next = r0kh->next;
+ else
+ *wpa_auth->conf.r0kh_list = r0kh->next;
+ if (r0kh->seq)
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ os_free(r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh, int timeout)
+{
+ if (timeout > 0)
+ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh, int timeout)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+}
+
+
+static struct ft_remote_r0kh *
+wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh_wildcard,
+ const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
+ int timeout)
+{
+ struct ft_remote_r0kh *r0kh;
+
+ if (!wpa_auth->conf.r0kh_list)
+ return NULL;
+
+ r0kh = os_zalloc(sizeof(*r0kh));
+ if (!r0kh)
+ return NULL;
+
+ if (src_addr)
+ os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+
+ if (id_len > FT_R0KH_ID_MAX_LEN)
+ id_len = FT_R0KH_ID_MAX_LEN;
+ os_memcpy(r0kh->id, r0kh_id, id_len);
+ r0kh->id_len = id_len;
+
+ os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+
+ r0kh->next = *wpa_auth->conf.r0kh_list;
+ *wpa_auth->conf.r0kh_list = r0kh;
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+
+ if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
+ return NULL;
+
+ return r0kh;
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+ if (!wpa_auth->conf.r1kh_list)
+ return;
+
+ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (r1kh == timeout_ctx)
+ break;
+ prev = r1kh;
+ }
+ if (!r1kh)
+ return;
+ if (prev)
+ prev->next = r1kh->next;
+ else
+ *wpa_auth->conf.r1kh_list = r1kh->next;
+ if (r1kh->seq)
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ os_free(r1kh);
+}
+
+
+static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r1kh *r1kh, int timeout)
+{
+ if (timeout > 0)
+ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+ wpa_auth, r1kh);
+}
+
+
+static struct ft_remote_r1kh *
+wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r1kh *r1kh_wildcard,
+ const u8 *src_addr, const u8 *r1kh_id, int timeout)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ if (!wpa_auth->conf.r1kh_list)
+ return NULL;
+
+ r1kh = os_zalloc(sizeof(*r1kh));
+ if (!r1kh)
+ return NULL;
+
+ os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+ os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+ os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+ r1kh->next = *wpa_auth->conf.r1kh_list;
+ *wpa_auth->conf.r1kh_list = r1kh;
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+ wpa_auth, r1kh);
+
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ return NULL;
+
+ return r1kh;
+}
+
+
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
+{
+ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+}
+
+
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh;
+ struct ft_remote_r1kh *r1kh;
+
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ for (; r0kh; r0kh = r0kh->next) {
+ if (!r0kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ r0kh->seq = NULL;
+ }
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ for (; r1kh; r1kh = r1kh->next) {
+ if (!r1kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ r1kh->seq = NULL;
+ }
+}
+
+
+static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
+ struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ while (r0kh) {
+ r0kh_next = r0kh->next;
+ if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
+ r0kh) > 0) {
+ if (r0kh_prev)
+ r0kh_prev->next = r0kh_next;
+ else
+ *wpa_auth->conf.r0kh_list = r0kh_next;
+ os_free(r0kh);
+ } else {
+ r0kh_prev = r0kh;
+ }
+ r0kh = r0kh_next;
+ }
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ while (r1kh) {
+ r1kh_next = r1kh->next;
+ if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
+ r1kh) > 0) {
+ if (r1kh_prev)
+ r1kh_prev->next = r1kh_next;
+ else
+ *wpa_auth->conf.r1kh_list = r1kh_next;
+ os_free(r1kh);
+ } else {
+ r1kh_prev = r1kh;
+ }
+ r1kh = r1kh_next;
+ }
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+ wpa_ft_deinit_seq(wpa_auth);
+ wpa_ft_deinit_rkh_tmp(wpa_auth);
+}
+
+
+static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+
+ if (!wpa_auth->conf.rkh_neg_timeout)
+ return;
+
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+
+ if (!r0kh_wildcard) {
+ /* r0kh removed after neg_timeout and might need re-adding */
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Temporarily block R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_neg_timeout);
+ os_memset(r0kh->addr, 0, ETH_ALEN);
+ } else
+ wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
+ f_r0kh_id_len,
+ wpa_auth->conf.rkh_neg_timeout);
+}
+
+
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
+ MAC2STR(sm->addr));
+ if (sm->ft_pending_pull_left_retries <= 0)
+ wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
+
+ /* cancel multiple timeouts */
+ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+ ft_finish_pull(sm);
+}
+
+
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *pmk_r0_name)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+ u8 *packet = NULL;
+ const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
+ size_t packet_len, key_len;
+ struct ft_rrb_seq f_seq;
+ int tsecs, tusecs, first;
+ struct wpabuf *ft_pending_req_ies;
+ int r0kh_timeout;
+ struct tlv_list req_enc[] = {
+ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+ .data = pmk_r0_name },
+ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+ .data = sm->addr },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+ struct tlv_list req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = sm->ft_pending_pull_nonce },
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
+ { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+ .data = sm->r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (sm->ft_pending_pull_left_retries <= 0)
+ return -1;
+ first = sm->ft_pending_pull_left_retries ==
+ sm->wpa_auth->conf.rkh_pull_retries;
+ sm->ft_pending_pull_left_retries--;
+
+ wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+
+ /* Keep r0kh sufficiently long in the list for seq num check */
+ r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
+ 1 + ftRRBseqTimeout;
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
+ } else if (r0kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+ /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
+ r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
+ r0kh_wildcard->addr,
+ sm->r0kh_id, sm->r0kh_id_len,
+ r0kh_timeout);
+ }
+ if (r0kh == NULL) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+ if (is_zero_ether_addr(r0kh->addr)) {
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is temporarily blocked",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+ if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: R0KH-ID points to self - no matching key available");
+ return -1;
+ }
+
+ key = r0kh->key;
+ key_len = sizeof(r0kh->key);
+
+ if (r0kh->seq->rx.num_last == 0) {
+ /* A sequence request will be sent out anyway when pull
+ * response is received. Send it out now to avoid one RTT. */
+ wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+ r0kh->id, r0kh->id_len, f_r1kh_id, key,
+ key_len, NULL, 0, NULL, 0, NULL);
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request from " MACSTR
+ " to remote R0KH address " MACSTR,
+ MAC2STR(sm->wpa_auth->addr), MAC2STR(r0kh->addr));
+
+ if (first &&
+ random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+ "nonce");
+ return -1;
+ }
+
+ if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
+ sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+ &packet, &packet_len) < 0)
+ return -1;
+
+ ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = ft_pending_req_ies;
+ if (!sm->ft_pending_req_ies) {
+ os_free(packet);
+ return -1;
+ }
+
+ tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
+ tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
+ eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
+ wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
+ packet, packet_len);
+
+ os_free(packet);
+
+ return 0;
+}
+
+
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
+ const u8 *pmk_r0, const u8 *pmk_r0_name)
+{
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
+ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+ SHA384_MAC_LEN : PMK_LEN;
+
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
+
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+ return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name, sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+}
+
+
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+ size_t *key_len, size_t kdk_len)
+{
+ size_t pmk_r0_len, pmk_r1_len;
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
+ const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
+ size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
+ const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
+ const u8 *ssid = sm->wpa_auth->conf.ssid;
+ size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+ const u8 *mpmk;
+ size_t mpmk_len;
+
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ (sm->xxkey_len == SHA256_MAC_LEN ||
+ sm->xxkey_len == SHA384_MAC_LEN ||
+ sm->xxkey_len == SHA512_MAC_LEN))
+ pmk_r0_len = sm->xxkey_len;
+ else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ pmk_r0_len = SHA384_MAC_LEN;
+ else
+ pmk_r0_len = PMK_LEN;
+ *key_len = pmk_r1_len = pmk_r0_len;
+
+ if (sm->xxkey_len > 0) {
+ mpmk = sm->xxkey;
+ mpmk_len = sm->xxkey_len;
+ } else if (sm->pmksa) {
+ mpmk = sm->pmksa->pmk;
+ mpmk_len = sm->pmksa->pmk_len;
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+ "derivation");
+ return -1;
+ }
+
+ if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
+ r0kh, r0kh_len, sm->addr,
+ pmk_r0, pmk_r0_name,
+ sm->wpa_key_mgmt) < 0 ||
+ wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
+ pmk_r1, sm->pmk_r1_name) < 0)
+ return -1;
+
+ return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
+ ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise,
+ kdk_len);
+}
+
+
+void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
+ const u8 *pmk_r1, const u8 *pmk_r0_name,
+ size_t key_len)
+{
+ int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
+
+ if (psk_local && wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+ return;
+
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return;
+ }
+
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+
+ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, key_len,
+ pmk_r0_name,
+ sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, key_len,
+ sm->pmk_r1_name, sm->pairwise, &vlan,
+ expires_in, session_timeout, identity,
+ identity_len, radius_cui, radius_cui_len);
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+{
+ if (wpa_auth->cb->get_seqnum == NULL)
+ return -1;
+ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+}
+
+
+static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+ u8 *subelem;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len, pad_len;
+ const u8 *key;
+ size_t key_len;
+ u8 keybuf[WPA_GTK_MAX_LEN];
+ const u8 *kek;
+ size_t kek_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
+
+ key_len = gsm->GTK_len;
+ if (key_len > sizeof(keybuf))
+ return NULL;
+
+ /*
+ * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
+ * than 16 bytes.
+ */
+ pad_len = key_len % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ if (key_len + pad_len < 16)
+ pad_len += 8;
+ if (pad_len && key_len < sizeof(keybuf)) {
+ os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+ if (conf->disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(keybuf, key_len) < 0)
+ return NULL;
+ }
+ os_memset(keybuf + key_len, 0, pad_len);
+ keybuf[key_len] = 0xdd;
+ key_len += pad_len;
+ key = keybuf;
+ } else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use of GTK
+ * in the BSS.
+ */
+ if (random_get_bytes(keybuf, key_len) < 0)
+ return NULL;
+ key = keybuf;
+ } else {
+ key = gsm->GTK[gsm->GN - 1];
+ }
+
+ /*
+ * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+ * Key[5..32].
+ */
+ subelem_len = 13 + key_len + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+
+ subelem[0] = FTIE_SUBELEM_GTK;
+ subelem[1] = 11 + key_len + 8;
+ /* Key ID in B0-B1 of Key Info */
+ WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+ subelem[4] = gsm->GTK_len;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+ if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: GTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
+ forced_memzero(keybuf, sizeof(keybuf));
+ os_free(subelem);
+ return NULL;
+ }
+
+ forced_memzero(keybuf, sizeof(keybuf));
+ *len = subelem_len;
+ return subelem;
+}
+
+
+static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+ u8 *subelem, *pos;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len;
+ const u8 *kek, *igtk;
+ size_t kek_len;
+ size_t igtk_len;
+ u8 stub_igtk[WPA_IGTK_MAX_LEN];
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
+
+ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+ /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+ * Key[16+8] */
+ subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+
+ pos = subelem;
+ *pos++ = FTIE_SUBELEM_IGTK;
+ *pos++ = subelem_len - 2;
+ WPA_PUT_LE16(pos, gsm->GN_igtk);
+ pos += 2;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+ pos += 6;
+ *pos++ = igtk_len;
+ igtk = gsm->IGTK[gsm->GN_igtk - 4];
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use of
+ * IGTK in the BSS.
+ */
+ if (random_get_bytes(stub_igtk, igtk_len / 8) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ igtk = stub_igtk;
+ }
+ if (aes_wrap(kek, kek_len, igtk_len / 8, igtk, pos)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: IGTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
+ os_free(subelem);
+ return NULL;
+ }
+
+ *len = subelem_len;
+ return subelem;
+}
+
+
+static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+ u8 *subelem, *pos;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len;
+ const u8 *kek, *bigtk;
+ size_t kek_len;
+ size_t bigtk_len;
+ u8 stub_bigtk[WPA_IGTK_MAX_LEN];
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
+
+ bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+ /* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
+ * Key[16+8] */
+ subelem_len = 1 + 1 + 2 + 6 + 1 + bigtk_len + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+
+ pos = subelem;
+ *pos++ = FTIE_SUBELEM_BIGTK;
+ *pos++ = subelem_len - 2;
+ WPA_PUT_LE16(pos, gsm->GN_bigtk);
+ pos += 2;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
+ pos += 6;
+ *pos++ = bigtk_len;
+ bigtk = gsm->IGTK[gsm->GN_bigtk - 6];
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random BIGTK to each OSEN STA to prevent use
+ * of BIGTK in the BSS.
+ */
+ if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ bigtk = stub_bigtk;
+ }
+ if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: BIGTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
+ os_free(subelem);
+ return NULL;
+ }
+
+ *len = subelem_len;
+ return subelem;
+}
+
+
+static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
+ u8 *pos, u8 *end, u8 id, u8 descr_count,
+ const u8 *ies, size_t ies_len)
+{
+ struct ieee802_11_elems parse;
+ struct rsn_rdie *rdie;
+
+ wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
+ id, descr_count);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
+ ies, ies_len);
+
+ if (end - pos < (int) sizeof(*rdie)) {
+ wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
+ return pos;
+ }
+
+ *pos++ = WLAN_EID_RIC_DATA;
+ *pos++ = sizeof(*rdie);
+ rdie = (struct rsn_rdie *) pos;
+ rdie->id = id;
+ rdie->descr_count = 0;
+ rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+ pos += sizeof(*rdie);
+
+ if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+ }
+
+ if (parse.wmm_tspec) {
+ struct wmm_tspec_element *tspec;
+
+ if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
+ wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
+ "(%d)", (int) parse.wmm_tspec_len);
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+ }
+ if (end - pos < (int) sizeof(*tspec)) {
+ wpa_printf(MSG_ERROR, "FT: Not enough room for "
+ "response TSPEC");
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+ }
+ tspec = (struct wmm_tspec_element *) pos;
+ os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+ }
+
+#ifdef NEED_AP_MLME
+ if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
+ int res;
+
+ res = wmm_process_tspec((struct wmm_tspec_element *) pos);
+ wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
+ if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
+ else if (res == WMM_ADDTS_STATUS_REFUSED)
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
+ else {
+ /* TSPEC accepted; include updated TSPEC in response */
+ rdie->descr_count = 1;
+ pos += sizeof(struct wmm_tspec_element);
+ }
+ return pos;
+ }
+#endif /* NEED_AP_MLME */
+
+ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+ int res;
+
+ res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+ sizeof(struct wmm_tspec_element));
+ if (res >= 0) {
+ if (res)
+ rdie->status_code = host_to_le16(res);
+ else {
+ /* TSPEC accepted; include updated TSPEC in
+ * response */
+ rdie->descr_count = 1;
+ pos += sizeof(struct wmm_tspec_element);
+ }
+ return pos;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
+ rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+}
+
+
+static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
+ const u8 *ric, size_t ric_len)
+{
+ const u8 *rpos, *start;
+ const struct rsn_rdie *rdie;
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
+
+ rpos = ric;
+ while (rpos + sizeof(*rdie) < ric + ric_len) {
+ if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
+ rpos + 2 + rpos[1] > ric + ric_len)
+ break;
+ rdie = (const struct rsn_rdie *) (rpos + 2);
+ rpos += 2 + rpos[1];
+ start = rpos;
+
+ while (rpos + 2 <= ric + ric_len &&
+ rpos + 2 + rpos[1] <= ric + ric_len) {
+ if (rpos[0] == WLAN_EID_RIC_DATA)
+ break;
+ rpos += 2 + rpos[1];
+ }
+ pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
+ rdie->descr_count,
+ start, rpos - start);
+ }
+
+ return pos;
+}
+
+
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+ size_t max_len, int auth_alg,
+ const u8 *req_ies, size_t req_ies_len,
+ int omit_rsnxe)
+{
+ u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+ u8 *fte_mic, *elem_count;
+ size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+ u8 rsnxe_buf[10], *rsnxe = rsnxe_buf;
+ size_t rsnxe_len;
+ int rsnxe_used;
+ int res;
+ struct wpa_auth_config *conf;
+ struct wpa_ft_ies parse;
+ u8 *ric_start;
+ u8 *anonce, *snonce;
+ const u8 *kck;
+ size_t kck_len;
+ size_t key_len;
+
+ if (sm == NULL)
+ return pos;
+
+ conf = &sm->wpa_auth->conf;
+
+ if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+ return pos;
+
+ end = pos + max_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth_alg == WLAN_AUTH_FT &&
+ sm->wpa_auth->conf.rsne_override_ft_set) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: RSNE FT override for MIC calculation");
+ rsnie = sm->wpa_auth->conf.rsne_override_ft;
+ rsnie_len = sm->wpa_auth->conf.rsne_override_ft_len;
+ if (end - pos < (long int) rsnie_len)
+ return pos;
+ os_memcpy(pos, rsnie, rsnie_len);
+ rsnie = pos;
+ pos += rsnie_len;
+ if (rsnie_len > PMKID_LEN && sm->pmk_r1_name_valid) {
+ int idx;
+
+ /* Replace all 0xff PMKID with the valid PMKR1Name */
+ for (idx = 0; idx < PMKID_LEN; idx++) {
+ if (rsnie[rsnie_len - 1 - idx] != 0xff)
+ break;
+ }
+ if (idx == PMKID_LEN)
+ os_memcpy(&rsnie[rsnie_len - PMKID_LEN],
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ }
+ } else
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (auth_alg == WLAN_AUTH_FT ||
+ ((auth_alg == WLAN_AUTH_FILS_SK ||
+ auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ auth_alg == WLAN_AUTH_FILS_PK) &&
+ (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384)))) {
+ if (!sm->pmk_r1_name_valid) {
+ wpa_printf(MSG_ERROR,
+ "FT: PMKR1Name is not valid for Assoc Resp RSNE");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ /*
+ * RSN (only present if this is a Reassociation Response and
+ * part of a fast BSS transition; or if this is a
+ * (Re)Association Response frame during an FT initial mobility
+ * domain association using FILS)
+ */
+ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
+ if (res < 0)
+ return NULL;
+ rsnie = pos;
+ rsnie_len = res;
+ pos += res;
+ }
+
+ /* Mobility Domain Information */
+ res = wpa_write_mdie(conf, pos, end - pos);
+ if (res < 0)
+ return NULL;
+ mdie = pos;
+ mdie_len = res;
+ pos += res;
+
+ /* Fast BSS Transition Information */
+ if (auth_alg == WLAN_AUTH_FT) {
+ subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+ if (!subelem) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add GTK subelement");
+ return NULL;
+ }
+ r0kh_id = sm->r0kh_id;
+ r0kh_id_len = sm->r0kh_id_len;
+ anonce = sm->ANonce;
+ snonce = sm->SNonce;
+ if (sm->mgmt_frame_prot) {
+ u8 *igtk;
+ size_t igtk_len;
+ u8 *nbuf;
+ igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+ if (igtk == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add IGTK subelement");
+ os_free(subelem);
+ return NULL;
+ }
+ nbuf = os_realloc(subelem, subelem_len + igtk_len);
+ if (nbuf == NULL) {
+ os_free(subelem);
+ os_free(igtk);
+ return NULL;
+ }
+ subelem = nbuf;
+ os_memcpy(subelem + subelem_len, igtk, igtk_len);
+ subelem_len += igtk_len;
+ os_free(igtk);
+ }
+ if (sm->mgmt_frame_prot && conf->beacon_prot) {
+ u8 *bigtk;
+ size_t bigtk_len;
+ u8 *nbuf;
+
+ bigtk = wpa_ft_bigtk_subelem(sm, &bigtk_len);
+ if (!bigtk) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add BIGTK subelement");
+ os_free(subelem);
+ return NULL;
+ }
+ nbuf = os_realloc(subelem, subelem_len + bigtk_len);
+ if (!nbuf) {
+ os_free(subelem);
+ os_free(bigtk);
+ return NULL;
+ }
+ subelem = nbuf;
+ os_memcpy(subelem + subelem_len, bigtk, bigtk_len);
+ subelem_len += bigtk_len;
+ os_free(bigtk);
+ }
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ u8 *nbuf, *ocipos;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element");
+ os_free(subelem);
+ return NULL;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->oci_freq_override_ft_assoc) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ conf->oci_freq_override_ft_assoc);
+ ci.frequency = conf->oci_freq_override_ft_assoc;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ subelem_len += 2 + OCV_OCI_LEN;
+ nbuf = os_realloc(subelem, subelem_len);
+ if (!nbuf) {
+ os_free(subelem);
+ return NULL;
+ }
+ subelem = nbuf;
+
+ ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
+ *ocipos++ = FTIE_SUBELEM_OCI;
+ *ocipos++ = OCV_OCI_LEN;
+ if (ocv_insert_oci(&ci, &ocipos) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
+ } else {
+ r0kh_id = conf->r0_key_holder;
+ r0kh_id_len = conf->r0_key_holder_len;
+ anonce = NULL;
+ snonce = NULL;
+ }
+ rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->wpa_auth->conf.ft_rsnxe_used) {
+ rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
+ wpa_printf(MSG_DEBUG, "TESTING: FT: Force RSNXE Used %d",
+ rsnxe_used);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ key_len = sm->xxkey_len;
+ if (!key_len)
+ key_len = sm->pmk_r1_len;
+ if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->wpa_auth->cb->get_psk) {
+ size_t psk_len;
+
+ if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
+ sm->addr, sm->p2p_dev_addr,
+ NULL, &psk_len, NULL))
+ key_len = psk_len;
+ }
+ res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
+ r0kh_id, r0kh_id_len,
+ anonce, snonce, pos, end - pos,
+ subelem, subelem_len, rsnxe_used);
+ os_free(subelem);
+ if (res < 0)
+ return NULL;
+ ftie = pos;
+ ftie_len = res;
+ pos += res;
+
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA512_MAC_LEN) {
+ struct rsn_ftie_sha512 *_ftie =
+ (struct rsn_ftie_sha512 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ key_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ struct rsn_ftie_sha384 *_ftie =
+ (struct rsn_ftie_sha384 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else {
+ struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ }
+ if (auth_alg == WLAN_AUTH_FT)
+ *elem_count = 3; /* Information element count */
+
+ ric_start = pos;
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
+ sm->wpa_key_mgmt) == 0 && parse.ric) {
+ pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+ parse.ric_len);
+ if (auth_alg == WLAN_AUTH_FT)
+ *elem_count +=
+ ieee802_11_ie_count(ric_start,
+ pos - ric_start);
+ }
+ if (ric_start == pos)
+ ric_start = NULL;
+
+ if (omit_rsnxe) {
+ rsnxe_len = 0;
+ } else {
+ res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe,
+ sizeof(rsnxe_buf));
+ if (res < 0)
+ return NULL;
+ rsnxe_len = res;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth_alg == WLAN_AUTH_FT &&
+ sm->wpa_auth->conf.rsnxe_override_ft_set) {
+ wpa_printf(MSG_DEBUG,
+ "TESTING: RSNXE FT override for MIC calculation");
+ rsnxe = sm->wpa_auth->conf.rsnxe_override_ft;
+ rsnxe_len = sm->wpa_auth->conf.rsnxe_override_ft_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (auth_alg == WLAN_AUTH_FT && rsnxe_len)
+ *elem_count += 1;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
+ if (auth_alg == WLAN_AUTH_FT &&
+ wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+ sm->addr, sm->wpa_auth->addr, 6,
+ mdie, mdie_len, ftie, ftie_len,
+ rsnie, rsnie_len,
+ ric_start, ric_start ? pos - ric_start : 0,
+ rsnxe_len ? rsnxe : NULL, rsnxe_len,
+ fte_mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return NULL;
+ }
+
+ os_free(sm->assoc_resp_ftie);
+ sm->assoc_resp_ftie = os_malloc(ftie_len);
+ if (!sm->assoc_resp_ftie)
+ return NULL;
+ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
+ return pos;
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+ int vlan_id,
+ enum wpa_alg alg, const u8 *addr, int idx,
+ u8 *key, size_t key_len,
+ enum key_flag key_flag)
+{
+ if (wpa_auth->cb->set_key == NULL)
+ return -1;
+ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+ key, key_len, key_flag);
+}
+
+
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+ const u8 *peer_addr,
+ const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len)
+{
+ if (!wpa_auth->cb->set_ltf_keyseed)
+ return -1;
+ return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+ ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
+static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
+ const u8 *addr)
+{
+ if (!wpa_auth->cb->add_sta_ft)
+ return -1;
+ return wpa_auth->cb->add_sta_ft(wpa_auth->cb_ctx, addr);
+}
+
+
+void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry)
+{
+ enum wpa_alg alg;
+ int klen;
+
+ /* MLME-SETKEYS.request(PTK) */
+ alg = wpa_cipher_to_alg(sm->pairwise);
+ klen = wpa_cipher_key_len(sm->pairwise);
+ if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
+ wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
+ "PTK configuration", sm->pairwise);
+ return;
+ }
+
+ if (sm->tk_already_set) {
+ /* Must avoid TK reconfiguration to prevent clearing of TX/RX
+ * PN in the driver */
+ wpa_printf(MSG_DEBUG,
+ "FT: Do not re-install same PTK to the driver");
+ return;
+ }
+
+ if (!retry)
+ wpa_auth_add_sta_ft(sm->wpa_auth, sm->addr);
+
+ /* FIX: add STA entry to kernel/driver here? The set_key will fail
+ * most likely without this.. At the moment, STA entry is added only
+ * after association has been completed. This function will be called
+ * again after association to get the PTK configured, but that could be
+ * optimized by adding the STA entry earlier.
+ */
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
+ sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
+ return;
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to set LTF keyseed to driver");
+ return;
+ }
+#endif /* CONFIG_PASN */
+
+ /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+ sm->pairwise_set = true;
+ sm->tk_already_set = true;
+
+ wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+ dot11RSNAConfigPMKLifetime, &sm->PTK);
+}
+
+
+/* Derive PMK-R1 from PSK, check all available PSK */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *req_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *out_vlan,
+ const u8 **out_identity, size_t *out_identity_len,
+ const u8 **out_radius_cui,
+ size_t *out_radius_cui_len,
+ int *out_session_timeout)
+{
+ const u8 *pmk = NULL;
+ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ const u8 *mdid = wpa_auth->conf.mobility_domain;
+ const u8 *r0kh = sm->r0kh_id;
+ size_t r0kh_len = sm->r0kh_id_len;
+ const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+ const u8 *ssid = wpa_auth->conf.ssid;
+ size_t ssid_len = wpa_auth->conf.ssid_len;
+ int pairwise;
+
+ pairwise = sm->pairwise;
+
+ for (;;) {
+ pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+ pmk);
+ if (pmk == NULL)
+ break;
+
+ if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+ r0kh_len, sm->addr,
+ pmk_r0, pmk_r0_name,
+ WPA_KEY_MGMT_FT_PSK) < 0 ||
+ wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
+ sm->addr, pmk_r1, pmk_r1_name) < 0 ||
+ os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0)
+ continue;
+
+ /* We found a PSK that matches the requested pmk_r1_name */
+ wpa_printf(MSG_DEBUG,
+ "FT: Found PSK to generate PMK-R1 locally");
+ os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+ if (out_pairwise)
+ *out_pairwise = pairwise;
+ os_memcpy(sm->PMK, pmk, PMK_LEN);
+ sm->pmk_len = PMK_LEN;
+ if (out_vlan &&
+ wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
+ MACSTR, MAC2STR(sm->addr));
+ return -1;
+ }
+
+ if (out_identity && out_identity_len) {
+ *out_identity_len = wpa_ft_get_identity(
+ sm->wpa_auth, sm->addr, out_identity);
+ }
+
+ if (out_radius_cui && out_radius_cui_len) {
+ *out_radius_cui_len = wpa_ft_get_radius_cui(
+ sm->wpa_auth, sm->addr, out_radius_cui);
+ }
+
+ if (out_session_timeout) {
+ *out_session_timeout = wpa_ft_get_session_timeout(
+ sm->wpa_auth, sm->addr);
+ }
+
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not find PSK to generate PMK-R1 locally");
+ return -1;
+}
+
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
+ struct wpa_ft_ies *parse)
+{
+ int key_mgmt, ciphers;
+
+ if (sm->wpa_key_mgmt)
+ return 0;
+
+ key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+ if (!key_mgmt) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
+ MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+ return -1;
+ }
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_FILS
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_FILS */
+ ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
+ if (!ciphers) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
+ MACSTR,
+ parse->pairwise_cipher, MAC2STR(sm->addr));
+ return -1;
+ }
+ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+ return 0;
+}
+
+
+static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *req_pmk_r0_name,
+ u8 *out_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui,
+ size_t *radius_cui_len,
+ int *out_session_timeout,
+ size_t *pmk_r1_len)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ int expires_in = 0;
+ int session_timeout = 0;
+ struct os_reltime now;
+
+ if (conf->r0_key_holder_len != r0kh_id_len ||
+ os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
+ 0)
+ return -1; /* not our R0KH-ID */
+
+ wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
+ if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
+ 0)
+ return -1; /* no matching PMKR0Name in local cache */
+
+ wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
+
+ if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
+ conf->r1_key_holder,
+ sm->addr, out_pmk_r1, out_pmk_r1_name) < 0)
+ return -1;
+
+ os_get_reltime(&now);
+ if (r0->expiration)
+ expires_in = r0->expiration - now.sec;
+
+ if (r0->session_timeout)
+ session_timeout = r0->session_timeout - now.sec;
+
+ wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
+ out_pmk_r1_name,
+ sm->pairwise, r0->vlan, expires_in, session_timeout,
+ r0->identity, r0->identity_len,
+ r0->radius_cui, r0->radius_cui_len);
+
+ *out_pairwise = sm->pairwise;
+ if (vlan) {
+ if (r0->vlan)
+ *vlan = *r0->vlan;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+ }
+
+ if (identity && identity_len) {
+ *identity = r0->identity;
+ *identity_len = r0->identity_len;
+ }
+
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r0->radius_cui;
+ *radius_cui_len = r0->radius_cui_len;
+ }
+
+ *out_session_timeout = session_timeout;
+
+ *pmk_r1_len = r0->pmk_r0_len;
+
+ return 0;
+}
+
+
+static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ u8 **resp_ies, size_t *resp_ies_len)
+{
+ struct rsn_mdie *mdie;
+ u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ struct wpa_auth_config *conf;
+ struct wpa_ft_ies parse;
+ size_t buflen;
+ int ret;
+ u8 *pos, *end;
+ int pairwise, session_timeout = 0;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len = 0, radius_cui_len = 0;
+ size_t pmk_r1_len, kdk_len, len;
+
+ *resp_ies = NULL;
+ *resp_ies_len = 0;
+
+ sm->pmk_r1_name_valid = 0;
+ conf = &sm->wpa_auth->conf;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
+ ies, ies_len);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain,
+ sm->wpa_auth->conf.mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return WLAN_STATUS_INVALID_MDIE;
+ }
+
+ if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
+ parse.r0kh_id, parse.r0kh_id_len);
+ os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+ sm->r0kh_id_len = parse.r0kh_id_len;
+
+ if (parse.rsn_pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
+ parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+
+ if (conf->ft_psk_generate_local &&
+ wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder,
+ sm->addr, pmk_r1_name, PMK_LEN) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) < 0)
+ return WLAN_STATUS_INVALID_PMKID;
+ pmk_r1_len = PMK_LEN;
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 for FT-PSK locally");
+ goto pmk_r1_derived;
+ }
+
+ /* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
+ * the key length is not yet known. For other AKMs, only the length
+ * identified by the AKM is used. */
+ for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
+ if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
+ len != SHA384_MAC_LEN) ||
+ (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
+ len != SHA256_MAC_LEN)))
+ continue;
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder,
+ sm->addr, pmk_r1_name, len) < 0)
+ continue;
+
+ if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+ pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+ &identity, &identity_len, &radius_cui,
+ &radius_cui_len,
+ &session_timeout) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Found PMKR1Name (using SHA%zu) from local cache",
+ pmk_r1_len * 8);
+ goto pmk_r1_derived;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+ parse.r0kh_id, parse.r0kh_id_len,
+ parse.rsn_pmkid,
+ pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout, &pmk_r1_len) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 based on local PMK-R0");
+ goto pmk_r1_derived;
+ }
+
+ if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ return -1; /* Status pending */
+
+pmk_r1_derived:
+ wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
+ sm->pmk_r1_name_valid = 1;
+ os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+ os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
+ sm->pmk_r1_len = pmk_r1_len;
+
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+ "ANonce");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ /* Now that we know the correct PMK-R1 length and as such, the length
+ * of the MIC field, fetch the SNonce. */
+ if (pmk_r1_len == SHA512_MAC_LEN) {
+ const struct rsn_ftie_sha512 *ftie;
+
+ ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else if (pmk_r1_len == SHA384_MAC_LEN) {
+ const struct rsn_ftie_sha384 *ftie;
+
+ ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else {
+ const struct rsn_ftie *ftie;
+
+ ftie = (const struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ sm->SNonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
+ sm->ANonce, WPA_NONCE_LEN);
+
+ if (sm->wpa_auth->conf.force_kdk_derivation ||
+ (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+ kdk_len = WPA_KDK_MAX_LEN;
+ else
+ kdk_len = 0;
+
+ if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, pmk_r1_name,
+ &sm->PTK, ptk_name, parse.key_mgmt,
+ pairwise, kdk_len) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_PASN */
+
+ sm->pairwise = pairwise;
+ sm->PTK_valid = true;
+ sm->tk_already_set = false;
+ wpa_ft_install_ptk(sm, 0);
+
+ if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
+ identity, identity_len) < 0 ||
+ wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
+ radius_cui, radius_cui_len) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
+
+ buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ 2 + FT_R1KH_ID_LEN + 200;
+ *resp_ies = os_zalloc(buflen);
+ if (*resp_ies == NULL)
+ goto fail;
+
+ pos = *resp_ies;
+ end = *resp_ies + buflen;
+
+ ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
+ if (ret < 0)
+ goto fail;
+ pos += ret;
+
+ ret = wpa_write_mdie(conf, pos, end - pos);
+ if (ret < 0)
+ goto fail;
+ pos += ret;
+
+ ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
+ parse.r0kh_id, parse.r0kh_id_len,
+ sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
+ 0);
+ if (ret < 0)
+ goto fail;
+ pos += ret;
+
+ *resp_ies_len = pos - *resp_ies;
+
+ return WLAN_STATUS_SUCCESS;
+fail:
+ os_free(*resp_ies);
+ *resp_ies = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+}
+
+
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+ u16 auth_transaction, const u8 *ies, size_t ies_len,
+ void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len),
+ void *ctx)
+{
+ u16 status;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ int res;
+
+ if (sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
+ "WPA SM not available");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
+ " BSSID=" MACSTR " transaction=%d",
+ MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+ sm->ft_pending_cb = cb;
+ sm->ft_pending_cb_ctx = ctx;
+ sm->ft_pending_auth_transaction = auth_transaction;
+ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
+ res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+ return;
+ }
+ status = res;
+
+ wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
+ " auth_transaction=%d status=%u (%s)",
+ MAC2STR(sm->addr), auth_transaction + 1, status,
+ status2str(status));
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+ cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+}
+
+
+int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len)
+{
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len;
+ unsigned int count;
+ const u8 *kck;
+ size_t kck_len;
+ struct wpa_auth_config *conf;
+
+ if (sm == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ conf = &sm->wpa_auth->conf;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (parse.rsn == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (parse.rsn_pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
+ "with the PMKR1Name derived from auth request");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, conf->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return WLAN_STATUS_INVALID_MDIE;
+ }
+
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA512_MAC_LEN)
+ mic_len = 32;
+ else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+ sm->pmk_r1_len == SHA384_MAC_LEN) ||
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+ mic_len = 24;
+ else
+ mic_len = 16;
+
+ if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+ parse.fte_mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+ parse.fte_mic_len, mic_len);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ parse.fte_snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sm->SNonce, WPA_NONCE_LEN);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+ parse.fte_anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+ sm->ANonce, WPA_NONCE_LEN);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+ os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+ "the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (os_memcmp_const(parse.r1kh_id, conf->r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+ "ReassocReq");
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
+ parse.r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+ conf->r1_key_holder, FT_R1KH_ID_LEN);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+ os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+ {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+ "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ count = 3;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+ if (parse.rsnxe)
+ count++;
+ if (parse.fte_elem_count != count) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+ "Control: received %u expected %u",
+ parse.fte_elem_count, count);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
+ if (wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+ sm->addr, sm->wpa_auth->addr, 5,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+ parse.ric, parse.ric_len,
+ parse.rsnxe ? parse.rsnxe - 2 : NULL,
+ parse.rsnxe ? parse.rsnxe_len + 2 : 0,
+ mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+ MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+ parse.fte_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+ parse.mdie - 2, parse.mdie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+ parse.ftie - 2, parse.ftie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+ parse.rsn - 2, parse.rsn_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
+ parse.rsnxe ? parse.rsnxe - 2 : NULL,
+ parse.rsnxe ? parse.rsnxe_len + 2 : 0);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (parse.fte_rsnxe_used &&
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH) &&
+ !parse.rsnxe) {
+ wpa_printf(MSG_INFO,
+ "FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
+ return -1; /* discard request */
+ }
+
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+ enum oci_verify_result res;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in (Re)Assoc Request");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx);
+ if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+ /* Work around misbehaving STAs */
+ wpa_printf(MSG_INFO,
+ "Disable OCV with a STA that does not send OCI");
+ wpa_auth_set_ocv(sm, 0);
+ } else if (res != OCI_SUCCESS) {
+ wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
+ if (sm->wpa_auth->conf.msg_ctx)
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+ OCV_FAILURE "addr=" MACSTR
+ " frame=ft-reassoc-req error=%s",
+ MAC2STR(sm->addr), ocv_errorstr);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
+{
+ const u8 *sta_addr, *target_ap;
+ const u8 *ies;
+ size_t ies_len;
+ u8 action;
+ struct ft_rrb_frame *frame;
+
+ if (sm == NULL)
+ return -1;
+
+ /*
+ * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+ * FT Request action frame body[variable]
+ */
+
+ if (len < 14) {
+ wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
+ "(len=%lu)", (unsigned long) len);
+ return -1;
+ }
+
+ action = data[1];
+ sta_addr = data + 2;
+ target_ap = data + 8;
+ ies = data + 14;
+ ies_len = len - 14;
+
+ wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
+ " Target AP=" MACSTR " Action=%d)",
+ MAC2STR(sta_addr), MAC2STR(target_ap), action);
+
+ if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
+ "STA=" MACSTR " STA-Address=" MACSTR,
+ MAC2STR(sm->addr), MAC2STR(sta_addr));
+ return -1;
+ }
+
+ /*
+ * Do some validity checking on the target AP address (not own and not
+ * broadcast. This could be extended to filter based on a list of known
+ * APs in the MD (if such a list were configured).
+ */
+ if ((target_ap[0] & 0x01) ||
+ os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
+ "frame");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+
+ if (!sm->wpa_auth->conf.ft_over_ds) {
+ wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
+ return -1;
+ }
+
+ /* RRB - Forward action frame to the target AP */
+ frame = os_malloc(sizeof(*frame) + len);
+ if (frame == NULL)
+ return -1;
+ frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+ frame->packet_type = FT_PACKET_REQUEST;
+ frame->action_length = host_to_le16(len);
+ os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
+ os_memcpy(frame + 1, data, len);
+
+ wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
+ sizeof(*frame) + len);
+ os_free(frame);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 resp,
+ const u8 *ies, size_t ies_len)
+{
+ struct wpa_state_machine *sm = ctx;
+ wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+ MAC2STR(sm->addr));
+ wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+ WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
+static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
+ const u8 *current_ap, const u8 *sta_addr,
+ const u8 *body, size_t len)
+{
+ struct wpa_state_machine *sm;
+ u16 status;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ int res;
+
+ sm = wpa_ft_add_sta(wpa_auth, sta_addr);
+ if (sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
+ "RRB Request");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
+
+ sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+ sm->ft_pending_cb_ctx = sm;
+ os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
+ res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+ return 0;
+ }
+ status = res;
+
+ res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+ return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ size_t rlen;
+ struct ft_rrb_frame *frame;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
+ " CurrentAP=" MACSTR " status=%u (%s)",
+ MAC2STR(sm->addr), MAC2STR(current_ap), status,
+ status2str(status));
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+
+ /* RRB - Forward action frame response to the Current AP */
+
+ /*
+ * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+ * Status_Code[2] FT Request action frame body[variable]
+ */
+ rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
+
+ frame = os_malloc(sizeof(*frame) + rlen);
+ if (frame == NULL)
+ return -1;
+ frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+ frame->packet_type = FT_PACKET_RESPONSE;
+ frame->action_length = host_to_le16(rlen);
+ os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
+ pos = (u8 *) (frame + 1);
+ *pos++ = WLAN_ACTION_FT;
+ *pos++ = 2; /* Action: Response */
+ os_memcpy(pos, sta_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ WPA_PUT_LE16(pos, status);
+ pos += 2;
+ if (resp_ies)
+ os_memcpy(pos, resp_ies, resp_ies_len);
+
+ wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
+ sizeof(*frame) + rlen);
+ os_free(frame);
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+ const struct tlv_list *tlvs,
+ const struct wpa_ft_pmk_r0_sa *pmk_r0,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ const struct tlv_list *tlv_auth,
+ const u8 *src_addr, u8 type,
+ u8 **packet, size_t *packet_len)
+{
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len = pmk_r0->pmk_r0_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 f_pairwise[sizeof(le16)];
+ u8 f_expires_in[sizeof(le16)];
+ u8 f_session_timeout[sizeof(le32)];
+ int expires_in;
+ int session_timeout;
+ struct os_reltime now;
+ int ret;
+ struct tlv_list sess_tlv[] = {
+ { .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
+ .data = pmk_r1 },
+ { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+ .data = pmk_r1_name },
+ { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+ .data = f_pairwise },
+ { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
+ .data = f_expires_in },
+ { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
+ .data = pmk_r0->identity },
+ { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
+ .data = pmk_r0->radius_cui },
+ { .type = FT_RRB_SESSION_TIMEOUT,
+ .len = sizeof(f_session_timeout),
+ .data = f_session_timeout },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 for peer AP");
+ if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
+ pmk_r0->pmk_r0_name, r1kh_id,
+ s1kh_id, pmk_r1, pmk_r1_name) < 0)
+ return -1;
+ WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+ os_get_reltime(&now);
+ if (pmk_r0->expiration > now.sec)
+ expires_in = pmk_r0->expiration - now.sec;
+ else if (pmk_r0->expiration)
+ expires_in = 1;
+ else
+ expires_in = 0;
+ WPA_PUT_LE16(f_expires_in, expires_in);
+
+ if (pmk_r0->session_timeout > now.sec)
+ session_timeout = pmk_r0->session_timeout - now.sec;
+ else if (pmk_r0->session_timeout)
+ session_timeout = 1;
+ else
+ session_timeout = 0;
+ WPA_PUT_LE32(f_session_timeout, session_timeout);
+
+ ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+ pmk_r0->vlan, src_addr, type,
+ packet, packet_len);
+
+ forced_memzero(pmk_r1, sizeof(pmk_r1));
+
+ return ret;
+}
+
+
+static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ const char *msgtype = "pull request";
+ u8 *plain = NULL, *packet = NULL;
+ size_t plain_len = 0, packet_len = 0;
+ struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
+ const u8 *key;
+ size_t key_len;
+ int seq_ret;
+ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+ size_t f_pmk_r0_name_len;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ int ret;
+ struct tlv_list resp[2];
+ struct tlv_list resp_auth[5];
+ struct ft_rrb_seq f_seq;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+ if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+ goto out;
+ }
+
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+ if (r1kh) {
+ key = r1kh->key;
+ key_len = sizeof(r1kh->key);
+ } else if (r1kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
+ key = r1kh_wildcard->key;
+ key_len = sizeof(r1kh_wildcard->key);
+ } else {
+ goto out;
+ }
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+ seq_ret = FT_RRB_SEQ_DROP;
+ if (r1kh)
+ seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
+ auth, auth_len, msgtype, no_defer);
+ if (!no_defer && r1kh_wildcard &&
+ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ /* wildcard: r1kh-id unknown or changed addr -> do a seq req */
+ seq_ret = FT_RRB_SEQ_DEFER;
+ }
+
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
+ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+ src_addr, FT_PACKET_R0KH_R1KH_PULL,
+ &plain, &plain_len) < 0)
+ goto out;
+
+ if (!r1kh)
+ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+ f_r1kh_id,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (!r1kh)
+ goto out;
+
+ if (seq_ret == FT_RRB_SEQ_DEFER) {
+ wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
+ f_r0kh_id_len, f_r1kh_id, key, key_len,
+ enc, enc_len, auth, auth_len,
+ &wpa_ft_rrb_rx_pull);
+ goto out;
+ }
+
+ wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+ msgtype);
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+ wpa_auth->conf.rkh_pos_timeout);
+
+ RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+ f_pmk_r0_name_len);
+
+ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull response from " MACSTR
+ " to " MACSTR,
+ MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
+
+ resp[0].type = FT_RRB_S1KH_ID;
+ resp[0].len = f_s1kh_id_len;
+ resp[0].data = f_s1kh_id;
+ resp[1].type = FT_RRB_LAST_EMPTY;
+ resp[1].len = 0;
+ resp[1].data = NULL;
+
+ resp_auth[0].type = FT_RRB_NONCE;
+ resp_auth[0].len = f_nonce_len;
+ resp_auth[0].data = f_nonce;
+ resp_auth[1].type = FT_RRB_SEQ;
+ resp_auth[1].len = sizeof(f_seq);
+ resp_auth[1].data = (u8 *) &f_seq;
+ resp_auth[2].type = FT_RRB_R0KH_ID;
+ resp_auth[2].len = f_r0kh_id_len;
+ resp_auth[2].data = f_r0kh_id;
+ resp_auth[3].type = FT_RRB_R1KH_ID;
+ resp_auth[3].len = f_r1kh_id_len;
+ resp_auth[3].data = f_r1kh_id;
+ resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ resp_auth[4].len = 0;
+ resp_auth[4].data = NULL;
+
+ if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+ ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
+ NULL, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ } else {
+ ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
+ f_s1kh_id, resp_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ }
+
+ if (!ret)
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_RESP, packet,
+ packet_len);
+
+out:
+ os_free(plain);
+ os_free(packet);
+
+ return 0;
+}
+
+
+/* @returns 0 on success
+ * -1 on error
+ * -2 if FR_RRB_PAIRWISE is missing
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, u8 type,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, u8 *s1kh_id_out,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+ const u8 *key;
+ size_t key_len;
+ int seq_ret;
+ const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+ const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+ const u8 *f_expires_in;
+ size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+ const u8 *f_identity, *f_radius_cui;
+ const u8 *f_session_timeout;
+ size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+ size_t f_expires_in_len;
+ size_t f_identity_len, f_radius_cui_len;
+ size_t f_session_timeout_len;
+ int pairwise;
+ int ret = -1;
+ int expires_in;
+ int session_timeout;
+ struct vlan_description vlan;
+ size_t pmk_r1_len;
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+ if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+ goto out;
+ }
+
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+ &r0kh_wildcard);
+ if (r0kh) {
+ key = r0kh->key;
+ key_len = sizeof(r0kh->key);
+ } else if (r0kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+ key = r0kh_wildcard->key;
+ key_len = sizeof(r0kh_wildcard->key);
+ } else {
+ goto out;
+ }
+
+ seq_ret = FT_RRB_SEQ_DROP;
+ if (r0kh) {
+ seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
+ auth, auth_len, msgtype,
+ cb ? 0 : 1);
+ }
+ if (cb && r0kh_wildcard &&
+ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ /* wildcard: r0kh-id unknown or changed addr -> do a seq req */
+ seq_ret = FT_RRB_SEQ_DEFER;
+ }
+
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
+ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ if (!r0kh)
+ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
+ f_r0kh_id, f_r0kh_id_len,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (!r0kh)
+ goto out;
+
+ if (seq_ret == FT_RRB_SEQ_DEFER) {
+ wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
+ f_r0kh_id_len, f_r1kh_id, key, key_len,
+ enc, enc_len, auth, auth_len, cb);
+ goto out;
+ }
+
+ wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+ msgtype);
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_pos_timeout);
+
+ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+ if (s1kh_id_out)
+ os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
+
+ ret = -2;
+ RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+ wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
+
+ ret = -1;
+ RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+ f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ pmk_r1_len = PMK_LEN;
+ if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
+ &f_pmk_r1) == 0 &&
+ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
+ f_pmk_r1_len == SHA512_MAC_LEN))
+ pmk_r1_len = f_pmk_r1_len;
+ RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
+
+ pairwise = WPA_GET_LE16(f_pairwise);
+
+ RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
+ sizeof(le16));
+ if (f_expires_in)
+ expires_in = WPA_GET_LE16(f_expires_in);
+ else
+ expires_in = 0;
+
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
+ expires_in);
+
+ if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
+ wpa_ft_rrb_dump(plain, plain_len);
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
+ le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
+
+ RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
+ if (f_identity)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
+ f_identity_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
+ if (f_radius_cui)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
+ f_radius_cui_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
+ sizeof(le32));
+ if (f_session_timeout)
+ session_timeout = WPA_GET_LE32(f_session_timeout);
+ else
+ session_timeout = 0;
+ wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
+
+ if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
+ f_pmk_r1_name,
+ pairwise, &vlan, expires_in, session_timeout,
+ f_identity, f_identity_len, f_radius_cui,
+ f_radius_cui_len) < 0)
+ goto out;
+
+ ret = 0;
+out:
+ bin_clear_free(plain, plain_len);
+
+ return ret;
+
+}
+
+
+static void ft_finish_pull(struct wpa_state_machine *sm)
+{
+ int res;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ u16 status;
+
+ if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
+ return;
+
+ res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+ wpabuf_len(sm->ft_pending_req_ies),
+ &resp_ies, &resp_ies_len);
+ if (res < 0) {
+ /* this loop is broken by ft_pending_pull_left_retries */
+ wpa_printf(MSG_DEBUG,
+ "FT: Callback postponed until response is available");
+ return;
+ }
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = NULL;
+ status = res;
+ wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+ " - status %u", MAC2STR(sm->addr), status);
+
+ sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+ sm->ft_pending_auth_transaction + 1, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+}
+
+
+struct ft_get_sta_ctx {
+ const u8 *nonce;
+ const u8 *s1kh_id;
+ struct wpa_state_machine *sm;
+};
+
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
+{
+ struct ft_get_sta_ctx *info = ctx;
+
+ if ((info->s1kh_id &&
+ os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+ os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+ return 0;
+
+ info->sm = sm;
+
+ return 1;
+}
+
+
+static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ const char *msgtype = "pull response";
+ int nak, ret = -1;
+ struct ft_get_sta_ctx ctx;
+ u8 s1kh_id[ETH_ALEN];
+ const u8 *f_nonce;
+ size_t f_nonce_len;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.nonce = f_nonce;
+ if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+ /* nonce not found */
+ wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
+ return -1;
+ }
+
+ ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
+ enc, enc_len, auth, auth_len, msgtype, s1kh_id,
+ no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+ if (ret == -2) {
+ ret = 0;
+ nak = 1;
+ } else {
+ nak = 0;
+ }
+ if (ret < 0)
+ return -1;
+
+ ctx.s1kh_id = s1kh_id;
+ if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Response to a pending pull request for " MACSTR,
+ MAC2STR(ctx.sm->addr));
+ eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
+ if (nak)
+ ctx.sm->ft_pending_pull_left_retries = 0;
+ ft_finish_pull(ctx.sm);
+ }
+
+out:
+ return ret;
+}
+
+
+static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len, int no_defer)
+{
+ const char *msgtype = "push";
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
+
+ if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+ enc, enc_len, auth, auth_len, msgtype, NULL,
+ no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, int type,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ struct ft_remote_seq **rkh_seq,
+ u8 **key, size_t *key_len,
+ struct ft_remote_r0kh **r0kh_out,
+ struct ft_remote_r1kh **r1kh_out,
+ struct ft_remote_r0kh **r0kh_wildcard_out,
+ struct ft_remote_r1kh **r1kh_wildcard_out)
+{
+ struct ft_remote_r0kh *r0kh = NULL;
+ struct ft_remote_r1kh *r1kh = NULL;
+ const u8 *f_r0kh_id, *f_r1kh_id;
+ size_t f_r0kh_id_len, f_r1kh_id_len;
+ int to_r0kh, to_r1kh;
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+ struct ft_remote_r0kh *r0kh_wildcard;
+ struct ft_remote_r1kh *r1kh_wildcard;
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+ to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+ to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+ if (to_r0kh && to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+ goto out;
+ }
+
+ if (!to_r0kh && !to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+ goto out;
+ }
+
+ if (!to_r0kh) {
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+ if (!r0kh_wildcard &&
+ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+ goto out;
+ }
+ if (r0kh) {
+ *key = r0kh->key;
+ *key_len = sizeof(r0kh->key);
+ } else {
+ *key = r0kh_wildcard->key;
+ *key_len = sizeof(r0kh_wildcard->key);
+ }
+ }
+
+ if (!to_r1kh) {
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+ &r1kh_wildcard);
+ if (!r1kh_wildcard &&
+ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+ f_r1kh_id, FT_R1KH_ID_LEN);
+ goto out;
+ }
+ if (r1kh) {
+ *key = r1kh->key;
+ *key_len = sizeof(r1kh->key);
+ } else {
+ *key = r1kh_wildcard->key;
+ *key_len = sizeof(r1kh_wildcard->key);
+ }
+ }
+
+ if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ os_free(plain);
+
+ if (!to_r0kh) {
+ if (!r0kh)
+ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
+ src_addr, f_r0kh_id,
+ f_r0kh_id_len,
+ ftRRBseqTimeout);
+ if (!r0kh)
+ goto out;
+
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
+ *rkh_seq = r0kh->seq;
+ if (r0kh_out)
+ *r0kh_out = r0kh;
+ if (r0kh_wildcard_out)
+ *r0kh_wildcard_out = r0kh_wildcard;
+ }
+
+ if (!to_r1kh) {
+ if (!r1kh)
+ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
+ src_addr, f_r1kh_id,
+ ftRRBseqTimeout);
+ if (!r1kh)
+ goto out;
+
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
+ *rkh_seq = r1kh->seq;
+ if (r1kh_out)
+ *r1kh_out = r1kh;
+ if (r1kh_wildcard_out)
+ *r1kh_wildcard_out = r1kh_wildcard;
+ }
+
+ return 0;
+out:
+ return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ int ret = -1;
+ struct ft_rrb_seq f_seq;
+ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
+ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ u8 *packet = NULL, *key = NULL;
+ size_t packet_len = 0, key_len = 0;
+ struct tlv_list seq_resp_auth[5];
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ enc, enc_len, auth, auth_len, &rkh_seq, &key,
+ &key_len, NULL, NULL, NULL, NULL) < 0)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+ if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Send sequence number response from " MACSTR
+ " to " MACSTR,
+ MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
+
+ seq_resp_auth[0].type = FT_RRB_NONCE;
+ seq_resp_auth[0].len = f_nonce_len;
+ seq_resp_auth[0].data = f_nonce;
+ seq_resp_auth[1].type = FT_RRB_SEQ;
+ seq_resp_auth[1].len = sizeof(f_seq);
+ seq_resp_auth[1].data = (u8 *) &f_seq;
+ seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+ seq_resp_auth[2].len = f_r0kh_id_len;
+ seq_resp_auth[2].data = f_r0kh_id;
+ seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+ seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+ seq_resp_auth[3].data = f_r1kh_id;
+ seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ seq_resp_auth[4].len = 0;
+ seq_resp_auth[4].data = NULL;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ &packet, &packet_len) < 0)
+ goto out;
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+ packet_len);
+
+out:
+ os_free(packet);
+
+ return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ u8 *key = NULL;
+ size_t key_len = 0;
+ struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
+ struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
+ const u8 *f_nonce, *f_seq;
+ size_t f_nonce_len, f_seq_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ struct ft_remote_item *item;
+ struct os_reltime now, now_remote;
+ int seq_ret, found;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_dom, msg_seq;
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ enc, enc_len, auth, auth_len, &rkh_seq, &key,
+ &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+ &r1kh_wildcard) < 0)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
+ f_nonce_len);
+
+ found = 0;
+ dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+ list) {
+ if (os_memcmp_const(f_nonce, item->nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ os_get_reltime(&now) < 0 ||
+ os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+ continue;
+
+ found = 1;
+ break;
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+ goto out;
+ }
+
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (r0kh_wildcard)
+ os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
+ }
+
+ if (r1kh) {
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (r1kh_wildcard)
+ os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
+ }
+
+ seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
+ auth_len, "seq response", 1);
+ if (seq_ret == FT_RRB_SEQ_OK) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+ wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+ auth_len, "seq response");
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+ sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_dom = le_to_host32(msg_both->dom);
+ msg_seq = le_to_host32(msg_both->seq);
+ now_remote.sec = le_to_host32(msg_both->ts);
+ now_remote.usec = 0;
+
+ rkh_seq->rx.num_last = 2;
+ rkh_seq->rx.dom = msg_dom;
+ rkh_seq->rx.offsetidx = 0;
+ /* Accept some older, possibly cached packets as well */
+ rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+ dl_list_len(&rkh_seq->rx.queue);
+ rkh_seq->rx.last[1] = msg_seq;
+
+ /* local time - offset = remote time
+ * <=> local time - remote time = offset */
+ os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+ }
+
+ wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
+
+ return 0;
+out:
+ return -1;
+}
+
+
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *data, size_t data_len)
+{
+ struct ft_rrb_frame *frame;
+ u16 alen;
+ const u8 *pos, *end, *start;
+ u8 action;
+ const u8 *sta_addr, *target_ap_addr;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
+ MAC2STR(src_addr));
+
+ if (data_len < sizeof(*frame)) {
+ wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
+ (unsigned long) data_len);
+ return -1;
+ }
+
+ pos = data;
+ frame = (struct ft_rrb_frame *) pos;
+ pos += sizeof(*frame);
+
+ alen = le_to_host16(frame->action_length);
+ wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
+ "action_length=%d ap_address=" MACSTR,
+ frame->frame_type, frame->packet_type, alen,
+ MAC2STR(frame->ap_address));
+
+ if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
+ /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
+ wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
+ "unrecognized type %d", frame->frame_type);
+ return -1;
+ }
+
+ if (alen > data_len - sizeof(*frame)) {
+ wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
+ "frame");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
+
+ if (alen < 1 + 1 + 2 * ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
+ "room for Action Frame body); alen=%lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ start = pos;
+ end = pos + alen;
+
+ if (*pos != WLAN_ACTION_FT) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
+ "%d", *pos);
+ return -1;
+ }
+
+ pos++;
+ action = *pos++;
+ sta_addr = pos;
+ pos += ETH_ALEN;
+ target_ap_addr = pos;
+ pos += ETH_ALEN;
+ wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
+ MACSTR " target_ap_addr=" MACSTR,
+ action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
+
+ if (frame->packet_type == FT_PACKET_REQUEST) {
+ wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
+
+ if (action != 1) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
+ "RRB Request", action);
+ return -1;
+ }
+
+ if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
+ "RRB Request does not match with own "
+ "address");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
+ sta_addr, pos, end - pos) < 0)
+ return -1;
+ } else if (frame->packet_type == FT_PACKET_RESPONSE) {
+ u16 status_code;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
+ "code in RRB Response");
+ return -1;
+ }
+ status_code = WPA_GET_LE16(pos);
+
+ wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
+ "(status_code=%d)", status_code);
+
+ if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
+ "packet_type %d", frame->packet_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+ size_t data_len)
+{
+ const u8 *auth, *enc;
+ size_t alen, elen;
+ int no_defer = 0;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI(" MACSTR
+ ") received frame from remote AP "
+ MACSTR " oui_suffix=%u dst=" MACSTR,
+ MAC2STR(wpa_auth->addr), MAC2STR(src_addr), oui_suffix,
+ MAC2STR(dst_addr));
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
+
+ if (is_multicast_ether_addr(src_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB-OUI received frame from multicast address "
+ MACSTR, MAC2STR(src_addr));
+ return;
+ }
+
+ if (is_multicast_ether_addr(dst_addr))
+ no_defer = 1;
+
+ if (data_len < sizeof(u16)) {
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+ return;
+ }
+
+ alen = WPA_GET_LE16(data);
+ if (data_len < sizeof(u16) + alen) {
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+ return;
+ }
+
+ auth = data + sizeof(u16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
+ enc = data + sizeof(u16) + alen;
+ elen = data_len - sizeof(u16) - alen;
+ wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
+
+ switch (oui_suffix) {
+ case FT_PACKET_R0KH_R1KH_PULL:
+ wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_RESP:
+ wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_PUSH:
+ wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+ alen, no_defer);
+ break;
+ }
+}
+
+
+static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_ft_pmk_r0_sa *pmk_r0,
+ struct ft_remote_r1kh *r1kh,
+ const u8 *s1kh_id)
+{
+ u8 *packet;
+ size_t packet_len;
+ struct ft_rrb_seq f_seq;
+ struct tlv_list push[] = {
+ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+ .data = s1kh_id },
+ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+ .data = pmk_r0->pmk_r0_name },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+ struct tlv_list push_auth[] = {
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
+ { .type = FT_RRB_R0KH_ID,
+ .len = wpa_auth->conf.r0_key_holder_len,
+ .data = wpa_auth->conf.r0_key_holder },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = r1kh->id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 push from " MACSTR
+ " to remote R0KH address " MACSTR,
+ MAC2STR(wpa_auth->addr), MAC2STR(r1kh->addr));
+
+ if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+ r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_PUSH,
+ &packet, &packet_len) < 0)
+ return -1;
+
+ wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
+ packet, packet_len);
+
+ os_free(packet);
+ return 0;
+}
+
+
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
+ struct ft_remote_r1kh *r1kh;
+
+ if (!wpa_auth->conf.pmk_r1_push)
+ return;
+ if (!wpa_auth->conf.r1kh_list)
+ return;
+
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ r0found = r0;
+ break;
+ }
+ }
+
+ r0 = r0found;
+ if (r0 == NULL || r0->pmk_r1_pushed)
+ return;
+ r0->pmk_r1_pushed = 1;
+
+ wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
+ "for STA " MACSTR, MAC2STR(addr));
+
+ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (is_zero_ether_addr(r1kh->addr) ||
+ is_zero_ether_addr(r1kh->id))
+ continue;
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ continue;
+ wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
+ }
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.c
new file mode 100644
index 0000000..17186bf
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.c
@@ -0,0 +1,1806 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "common/ptksa_cache.h"
+#include "crypto/sha1.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "eap_server/eap.h"
+#include "l2_packet/l2_packet.h"
+#include "eth_p_oui.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "preauth_auth.h"
+#include "sta_info.h"
+#include "tkip_countermeasures.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth.h"
+#include "wpa_auth_glue.h"
+
+
+static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+ struct hostapd_config *iconf,
+ struct wpa_auth_config *wconf)
+{
+ int sae_pw_id;
+
+ os_memset(wconf, 0, sizeof(*wconf));
+ wconf->wpa = conf->wpa;
+ wconf->extended_key_id = conf->extended_key_id;
+ wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+ wconf->wpa_pairwise = conf->wpa_pairwise;
+ wconf->wpa_group = conf->wpa_group;
+ wconf->wpa_group_rekey = conf->wpa_group_rekey;
+ wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
+ wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+ wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+ wconf->wpa_group_update_count = conf->wpa_group_update_count;
+ wconf->wpa_disable_eapol_key_retries =
+ conf->wpa_disable_eapol_key_retries;
+ wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
+ wconf->rsn_pairwise = conf->rsn_pairwise;
+ wconf->rsn_preauth = conf->rsn_preauth;
+ wconf->eapol_version = conf->eapol_version;
+#ifdef CONFIG_MACSEC
+ if (wconf->eapol_version > 2)
+ wconf->eapol_version = 2;
+#endif /* CONFIG_MACSEC */
+ wconf->wmm_enabled = conf->wmm_enabled;
+ wconf->wmm_uapsd = conf->wmm_uapsd;
+ wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
+#ifdef CONFIG_OCV
+ wconf->ocv = conf->ocv;
+#endif /* CONFIG_OCV */
+ wconf->okc = conf->okc;
+ wconf->ieee80211w = conf->ieee80211w;
+ wconf->beacon_prot = conf->beacon_prot;
+ wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
+ wconf->sae_require_mfp = conf->sae_require_mfp;
+#ifdef CONFIG_IEEE80211R_AP
+ wconf->ssid_len = conf->ssid.ssid_len;
+ if (wconf->ssid_len > SSID_MAX_LEN)
+ wconf->ssid_len = SSID_MAX_LEN;
+ os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+ os_memcpy(wconf->mobility_domain, conf->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ if (conf->nas_identifier &&
+ os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) {
+ wconf->r0_key_holder_len = os_strlen(conf->nas_identifier);
+ os_memcpy(wconf->r0_key_holder, conf->nas_identifier,
+ wconf->r0_key_holder_len);
+ }
+ os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
+ wconf->r0_key_lifetime = conf->r0_key_lifetime;
+ wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
+ wconf->reassociation_deadline = conf->reassociation_deadline;
+ wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+ wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
+ wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
+ wconf->rkh_pull_retries = conf->rkh_pull_retries;
+ wconf->r0kh_list = &conf->r0kh_list;
+ wconf->r1kh_list = &conf->r1kh_list;
+ wconf->pmk_r1_push = conf->pmk_r1_push;
+ wconf->ft_over_ds = conf->ft_over_ds;
+ wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_HS20
+ wconf->disable_gtk = conf->disable_dgaf;
+ if (conf->osen) {
+ wconf->disable_gtk = 1;
+ wconf->wpa = WPA_PROTO_OSEN;
+ wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+ wconf->wpa_pairwise = 0;
+ wconf->wpa_group = WPA_CIPHER_CCMP;
+ wconf->rsn_pairwise = WPA_CIPHER_CCMP;
+ wconf->rsn_preauth = 0;
+ wconf->disable_pmksa_caching = 1;
+ wconf->ieee80211w = 1;
+ }
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_TESTING_OPTIONS
+ wconf->corrupt_gtk_rekey_mic_probability =
+ iconf->corrupt_gtk_rekey_mic_probability;
+ wconf->delay_eapol_tx = iconf->delay_eapol_tx;
+ if (conf->own_ie_override &&
+ wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
+ os_memcpy(wconf->own_ie_override,
+ wpabuf_head(conf->own_ie_override),
+ wconf->own_ie_override_len);
+ }
+ if (conf->rsne_override_eapol &&
+ wpabuf_len(conf->rsne_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsne_override_eapol_set = 1;
+ wconf->rsne_override_eapol_len =
+ wpabuf_len(conf->rsne_override_eapol);
+ os_memcpy(wconf->rsne_override_eapol,
+ wpabuf_head(conf->rsne_override_eapol),
+ wconf->rsne_override_eapol_len);
+ }
+ if (conf->rsnxe_override_eapol &&
+ wpabuf_len(conf->rsnxe_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsnxe_override_eapol_set = 1;
+ wconf->rsnxe_override_eapol_len =
+ wpabuf_len(conf->rsnxe_override_eapol);
+ os_memcpy(wconf->rsnxe_override_eapol,
+ wpabuf_head(conf->rsnxe_override_eapol),
+ wconf->rsnxe_override_eapol_len);
+ }
+ if (conf->rsne_override_ft &&
+ wpabuf_len(conf->rsne_override_ft) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsne_override_ft_set = 1;
+ wconf->rsne_override_ft_len =
+ wpabuf_len(conf->rsne_override_ft);
+ os_memcpy(wconf->rsne_override_ft,
+ wpabuf_head(conf->rsne_override_ft),
+ wconf->rsne_override_ft_len);
+ }
+ if (conf->rsnxe_override_ft &&
+ wpabuf_len(conf->rsnxe_override_ft) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->rsnxe_override_ft_set = 1;
+ wconf->rsnxe_override_ft_len =
+ wpabuf_len(conf->rsnxe_override_ft);
+ os_memcpy(wconf->rsnxe_override_ft,
+ wpabuf_head(conf->rsnxe_override_ft),
+ wconf->rsnxe_override_ft_len);
+ }
+ if (conf->gtk_rsc_override &&
+ wpabuf_len(conf->gtk_rsc_override) > 0 &&
+ wpabuf_len(conf->gtk_rsc_override) <= WPA_KEY_RSC_LEN) {
+ os_memcpy(wconf->gtk_rsc_override,
+ wpabuf_head(conf->gtk_rsc_override),
+ wpabuf_len(conf->gtk_rsc_override));
+ wconf->gtk_rsc_override_set = 1;
+ }
+ if (conf->igtk_rsc_override &&
+ wpabuf_len(conf->igtk_rsc_override) > 0 &&
+ wpabuf_len(conf->igtk_rsc_override) <= WPA_KEY_RSC_LEN) {
+ os_memcpy(wconf->igtk_rsc_override,
+ wpabuf_head(conf->igtk_rsc_override),
+ wpabuf_len(conf->igtk_rsc_override));
+ wconf->igtk_rsc_override_set = 1;
+ }
+ wconf->ft_rsnxe_used = conf->ft_rsnxe_used;
+ wconf->oci_freq_override_eapol_m3 = conf->oci_freq_override_eapol_m3;
+ wconf->oci_freq_override_eapol_g1 = conf->oci_freq_override_eapol_g1;
+ wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
+ wconf->oci_freq_override_fils_assoc =
+ conf->oci_freq_override_fils_assoc;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+ os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+ os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+ os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+ os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+ wconf->fils_cache_id_set = conf->fils_cache_id_set;
+ os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
+ FILS_CACHE_ID_LEN);
+#endif /* CONFIG_FILS */
+ wconf->sae_pwe = conf->sae_pwe;
+ sae_pw_id = hostapd_sae_pw_id_in_use(conf);
+ if (sae_pw_id == 2 && wconf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+ wconf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+ else if (sae_pw_id == 1 && wconf->sae_pwe == SAE_PWE_HUNT_AND_PECK)
+ wconf->sae_pwe = SAE_PWE_BOTH;
+#ifdef CONFIG_SAE_PK
+ wconf->sae_pk = hostapd_sae_pk_in_use(conf);
+#endif /* CONFIG_SAE_PK */
+#ifdef CONFIG_OWE
+ wconf->owe_ptk_workaround = conf->owe_ptk_workaround;
+#endif /* CONFIG_OWE */
+ wconf->transition_disable = conf->transition_disable;
+#ifdef CONFIG_DPP2
+ wconf->dpp_pfs = conf->dpp_pfs;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_PASN
+#ifdef CONFIG_TESTING_OPTIONS
+ wconf->force_kdk_derivation = conf->force_kdk_derivation;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_PASN */
+
+ wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS;
+}
+
+
+static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
+ logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+ struct hostapd_data *hapd = ctx;
+ int hlevel;
+
+ switch (level) {
+ case LOGGER_WARNING:
+ hlevel = HOSTAPD_LEVEL_WARNING;
+ break;
+ case LOGGER_INFO:
+ hlevel = HOSTAPD_LEVEL_INFO;
+ break;
+ case LOGGER_DEBUG:
+ default:
+ hlevel = HOSTAPD_LEVEL_DEBUG;
+ break;
+ }
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
+ u16 reason)
+{
+ struct hostapd_data *hapd = ctx;
+ wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+ "STA " MACSTR " reason %d",
+ __func__, MAC2STR(addr), reason);
+ ap_sta_disconnect(hapd, NULL, addr, reason);
+}
+
+
+static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ return michael_mic_failure(hapd, addr, 0);
+}
+
+
+static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+ MAC2STR(addr));
+ hostapd_ubus_notify(hapd, "key-mismatch", addr);
+}
+
+
+static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
+ wpa_eapol_variable var, int value)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta == NULL)
+ return;
+ switch (var) {
+ case WPA_EAPOL_portEnabled:
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, value);
+ break;
+ case WPA_EAPOL_portValid:
+ ieee802_1x_notify_port_valid(sta->eapol_sm, value);
+ break;
+ case WPA_EAPOL_authorized:
+ ieee802_1x_set_sta_authorized(hapd, sta, value);
+ break;
+ case WPA_EAPOL_portControl_Auto:
+ if (sta->eapol_sm)
+ sta->eapol_sm->portControl = Auto;
+ break;
+ case WPA_EAPOL_keyRun:
+ if (sta->eapol_sm)
+ sta->eapol_sm->keyRun = value;
+ break;
+ case WPA_EAPOL_keyAvailable:
+ if (sta->eapol_sm)
+ sta->eapol_sm->eap_if->eapKeyAvailable = value;
+ break;
+ case WPA_EAPOL_keyDone:
+ if (sta->eapol_sm)
+ sta->eapol_sm->keyDone = value;
+ break;
+ case WPA_EAPOL_inc_EapolFramesTx:
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapolFramesTx++;
+ break;
+ }
+}
+
+
+static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
+ wpa_eapol_variable var)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta == NULL || sta->eapol_sm == NULL)
+ return -1;
+ switch (var) {
+ case WPA_EAPOL_keyRun:
+ return sta->eapol_sm->keyRun;
+ case WPA_EAPOL_keyAvailable:
+ return sta->eapol_sm->eap_if->eapKeyAvailable;
+ default:
+ return -1;
+ }
+}
+
+
+static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *p2p_dev_addr,
+ const u8 *prev_psk, size_t *psk_len,
+ int *vlan_id)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ const u8 *psk;
+
+ if (vlan_id)
+ *vlan_id = 0;
+ if (psk_len)
+ *psk_len = PMK_LEN;
+
+#ifdef CONFIG_SAE
+ if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+ if (!sta->sae || prev_psk)
+ return NULL;
+ if (psk_len)
+ *psk_len = sta->sae->pmk_len;
+ return sta->sae->pmk;
+ }
+ if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG,
+ "No PSK for STA trying to use SAE with PMKSA caching");
+ return NULL;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_pmk) {
+ if (psk_len)
+ *psk_len = sta->owe_pmk_len;
+ return sta->owe_pmk;
+ }
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+ struct rsn_pmksa_cache_entry *sa;
+
+ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
+ if (psk_len)
+ *psk_len = sa->pmk_len;
+ return sa->pmk;
+ }
+ }
+#endif /* CONFIG_OWE */
+
+ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk,
+ vlan_id);
+ /*
+ * This is about to iterate over all psks, prev_psk gives the last
+ * returned psk which should not be returned again.
+ * logic list (all hostapd_get_psk; all sta->psk)
+ */
+ if (sta && sta->psk && !psk) {
+ struct hostapd_sta_wpa_psk_short *pos;
+
+ if (vlan_id)
+ *vlan_id = 0;
+ psk = sta->psk->psk;
+ for (pos = sta->psk; pos; pos = pos->next) {
+ if (pos->is_passphrase) {
+ if (pbkdf2_sha1(pos->passphrase,
+ hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len, 4096,
+ pos->psk, PMK_LEN) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Error in pbkdf2_sha1()");
+ continue;
+ }
+ pos->is_passphrase = 0;
+ }
+ if (pos->psk == prev_psk) {
+ psk = pos->next ? pos->next->psk : NULL;
+ break;
+ }
+ }
+ }
+ return psk;
+}
+
+
+static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
+ size_t *len)
+{
+ struct hostapd_data *hapd = ctx;
+ const u8 *key;
+ size_t keylen;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA");
+ return -1;
+ }
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+ if (key == NULL) {
+ wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
+ sta->eapol_sm);
+ return -1;
+ }
+
+ if (keylen > *len)
+ keylen = *len;
+ os_memcpy(msk, key, keylen);
+ *len = keylen;
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+ const u8 *addr, int idx, u8 *key,
+ size_t key_len, enum key_flag key_flag)
+{
+ struct hostapd_data *hapd = ctx;
+ const char *ifname = hapd->conf->iface;
+
+ if (vlan_id > 0) {
+ ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+ if (!ifname) {
+ if (!(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_VLAN_OFFLOAD))
+ return -1;
+ ifname = hapd->conf->iface;
+ }
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (key_flag & KEY_FLAG_MODIFY) {
+ /* We are updating an already installed key. Don't overwrite
+ * the already stored key information with zeros.
+ */
+ } else if (addr && !is_broadcast_ether_addr(addr)) {
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ sta->last_tk_alg = alg;
+ sta->last_tk_key_idx = idx;
+ if (key)
+ os_memcpy(sta->last_tk, key, key_len);
+ sta->last_tk_len = key_len;
+ }
+ } else if (alg == WPA_ALG_BIP_CMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256) {
+ if (idx == 4 || idx == 5) {
+ hapd->last_igtk_alg = alg;
+ hapd->last_igtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_igtk, key, key_len);
+ hapd->last_igtk_len = key_len;
+ } else if (idx == 6 || idx == 7) {
+ hapd->last_bigtk_alg = alg;
+ hapd->last_bigtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_bigtk, key, key_len);
+ hapd->last_bigtk_len = key_len;
+ }
+ } else {
+ hapd->last_gtk_alg = alg;
+ hapd->last_gtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_gtk, key, key_len);
+ hapd->last_gtk_len = key_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1,
+ NULL, 0, key, key_len, key_flag);
+}
+
+
+static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx,
+ u8 *seq)
+{
+ struct hostapd_data *hapd = ctx;
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && idx)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+ return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, link_id,
+ seq);
+}
+
+
+int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
+ const u8 *data, size_t data_len,
+ int encrypt)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ u32 flags = 0;
+ int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+ link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_eapol_frame_io) {
+ size_t hex_len = 2 * data_len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex == NULL)
+ return -1;
+ wpa_snprintf_hex(hex, hex_len, data, data_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+ MAC2STR(addr), hex);
+ os_free(hex);
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ flags = hostapd_sta_flags_to_drv(sta->flags);
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta && (sta->flags & WLAN_STA_AUTHORIZED))
+ link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
+ }
+
+ return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
+ encrypt, flags, link_id);
+}
+
+
+static int hostapd_wpa_auth_for_each_sta(
+ void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx),
+ void *cb_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx))
+ return 1;
+ }
+ return 0;
+}
+
+
+struct wpa_auth_iface_iter_data {
+ int (*cb)(struct wpa_authenticator *sm, void *ctx);
+ void *cb_ctx;
+};
+
+static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct wpa_auth_iface_iter_data *data = ctx;
+ size_t i;
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->wpa_auth &&
+ data->cb(iface->bss[i]->wpa_auth, data->cb_ctx))
+ return 1;
+ }
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_for_each_auth(
+ void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
+ void *cb_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct wpa_auth_iface_iter_data data;
+ if (hapd->iface->interfaces == NULL ||
+ hapd->iface->interfaces->for_each_interface == NULL)
+ return -1;
+ data.cb = cb;
+ data.cb_ctx = cb_ctx;
+ return hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, wpa_auth_iface_iter, &data);
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct wpa_ft_rrb_rx_later_data {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ size_t data_len;
+ /* followed by data_len octets of data */
+};
+
+static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct wpa_ft_rrb_rx_later_data *data, *n;
+
+ dl_list_for_each_safe(data, n, &hapd->l2_queue,
+ struct wpa_ft_rrb_rx_later_data, list) {
+ if (hapd->wpa_auth) {
+ wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
+ (const u8 *) (data + 1),
+ data->data_len);
+ }
+ dl_list_del(&data->list);
+ os_free(data);
+ }
+}
+
+
+struct wpa_auth_ft_iface_iter_data {
+ struct hostapd_data *src_hapd;
+ const u8 *dst;
+ const u8 *data;
+ size_t data_len;
+};
+
+
+static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct wpa_auth_ft_iface_iter_data *idata = ctx;
+ struct wpa_ft_rrb_rx_later_data *data;
+ struct hostapd_data *hapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hapd == idata->src_hapd ||
+ !hapd->wpa_auth ||
+ os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "FT: Send RRB data directly to locally managed BSS "
+ MACSTR "@%s -> " MACSTR "@%s",
+ MAC2STR(idata->src_hapd->own_addr),
+ idata->src_hapd->conf->iface,
+ MAC2STR(hapd->own_addr), hapd->conf->iface);
+
+ /* Defer wpa_ft_rrb_rx() until next eloop step as this is
+ * when it would be triggered when reading from a socket.
+ * This avoids
+ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+ * that is calling hapd0:recv handler from within
+ * hapd0:send directly.
+ */
+ data = os_zalloc(sizeof(*data) + idata->data_len);
+ if (!data)
+ return 1;
+
+ os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
+ os_memcpy(data + 1, idata->data, idata->data_len);
+ data->data_len = idata->data_len;
+
+ dl_list_add(&hapd->l2_queue, &data->list);
+
+ if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
+ hapd, NULL))
+ eloop_register_timeout(0, 0,
+ hostapd_wpa_ft_rrb_rx_later,
+ hapd, NULL);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
+ const u8 *data, size_t data_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct l2_ethhdr *buf;
+ int ret;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+ size_t hex_len = 2 * data_len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex == NULL)
+ return -1;
+ wpa_snprintf_hex(hex, hex_len, data, data_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+ MAC2STR(dst), hex);
+ os_free(hex);
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (proto == ETH_P_RRB && hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface) {
+ int res;
+ struct wpa_auth_ft_iface_iter_data idata;
+ idata.src_hapd = hapd;
+ idata.dst = dst;
+ idata.data = data;
+ idata.data_len = data_len;
+ res = hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_wpa_auth_ft_iter,
+ &idata);
+ if (res == 1)
+ return data_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ if (hapd->l2 == NULL)
+ return -1;
+
+ buf = os_malloc(sizeof(*buf) + data_len);
+ if (buf == NULL)
+ return -1;
+ os_memcpy(buf->h_dest, dst, ETH_ALEN);
+ os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
+ buf->h_proto = host_to_be16(proto);
+ os_memcpy(buf + 1, data, data_len);
+ ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
+ sizeof(*buf) + data_len);
+ os_free(buf);
+ return ret;
+}
+
+
+#ifdef CONFIG_ETH_P_OUI
+static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
+ u8 oui_suffix)
+{
+ switch (oui_suffix) {
+#ifdef CONFIG_IEEE80211R_AP
+ case FT_PACKET_R0KH_R1KH_PULL:
+ return hapd->oui_pull;
+ case FT_PACKET_R0KH_R1KH_RESP:
+ return hapd->oui_resp;
+ case FT_PACKET_R0KH_R1KH_PUSH:
+ return hapd->oui_push;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ return hapd->oui_sreq;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ return hapd->oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+ default:
+ return NULL;
+ }
+}
+#endif /* CONFIG_ETH_P_OUI */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct oui_deliver_later_data {
+ struct dl_list list;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+ size_t data_len;
+ u8 oui_suffix;
+ /* followed by data_len octets of data */
+};
+
+static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct oui_deliver_later_data *data, *n;
+ struct eth_p_oui_ctx *oui_ctx;
+
+ dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
+ struct oui_deliver_later_data, list) {
+ oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
+ wpa_printf(MSG_DEBUG, "RRB(%s): %s src=" MACSTR " dst=" MACSTR
+ " oui_suffix=%u data_len=%u data=%p",
+ hapd->conf->iface, __func__,
+ MAC2STR(data->src_addr), MAC2STR(data->dst_addr),
+ data->oui_suffix, (unsigned int) data->data_len,
+ data);
+ if (hapd->wpa_auth && oui_ctx) {
+ eth_p_oui_deliver(oui_ctx, data->src_addr,
+ data->dst_addr,
+ (const u8 *) (data + 1),
+ data->data_len);
+ }
+ dl_list_del(&data->list);
+ os_free(data);
+ }
+}
+
+
+struct wpa_auth_oui_iface_iter_data {
+ struct hostapd_data *src_hapd;
+ const u8 *dst_addr;
+ const u8 *data;
+ size_t data_len;
+ u8 oui_suffix;
+};
+
+static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct wpa_auth_oui_iface_iter_data *idata = ctx;
+ struct oui_deliver_later_data *data;
+ struct hostapd_data *hapd, *src_hapd = idata->src_hapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hapd == src_hapd)
+ continue; /* don't deliver back to same interface */
+ if (!wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) ||
+ hapd->conf->ssid.ssid_len !=
+ src_hapd->conf->ssid.ssid_len ||
+ os_memcmp(hapd->conf->ssid.ssid,
+ src_hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len) != 0 ||
+ os_memcmp(hapd->conf->mobility_domain,
+ src_hapd->conf->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0)
+ continue; /* no matching FT SSID/mobility domain */
+ if (!is_multicast_ether_addr(idata->dst_addr) &&
+ os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+ continue; /* destination address does not match */
+
+ /* defer eth_p_oui_deliver until next eloop step as this is
+ * when it would be triggerd from reading from sock
+ * This avoids
+ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+ * that is calling hapd0:recv handler from within
+ * hapd0:send directly.
+ */
+ data = os_zalloc(sizeof(*data) + idata->data_len);
+ if (!data)
+ return 1;
+ wpa_printf(MSG_DEBUG,
+ "RRB(%s): local delivery to %s dst=" MACSTR
+ " oui_suffix=%u data_len=%u data=%p",
+ src_hapd->conf->iface, hapd->conf->iface,
+ MAC2STR(idata->dst_addr), idata->oui_suffix,
+ (unsigned int) idata->data_len, data);
+
+ os_memcpy(data->src_addr, src_hapd->own_addr, ETH_ALEN);
+ os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
+ os_memcpy(data + 1, idata->data, idata->data_len);
+ data->data_len = idata->data_len;
+ data->oui_suffix = idata->oui_suffix;
+
+ dl_list_add_tail(&hapd->l2_oui_queue, &data->list);
+
+ if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
+ hapd, NULL))
+ eloop_register_timeout(0, 0,
+ hostapd_oui_deliver_later,
+ hapd, NULL);
+
+ /* If dst_addr is a multicast address, do not return any
+ * non-zero value here. Otherwise, the iteration of
+ * for_each_interface() will be stopped. */
+ if (!is_multicast_ether_addr(idata->dst_addr))
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
+ const u8 *data, size_t data_len)
+{
+#ifdef CONFIG_ETH_P_OUI
+ struct hostapd_data *hapd = ctx;
+ struct eth_p_oui_ctx *oui_ctx;
+
+ wpa_printf(MSG_DEBUG, "RRB(%s): send to dst=" MACSTR
+ " oui_suffix=%u data_len=%u",
+ hapd->conf->iface, MAC2STR(dst), oui_suffix,
+ (unsigned int) data_len);
+#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface) {
+ struct wpa_auth_oui_iface_iter_data idata;
+ int res;
+
+ idata.src_hapd = hapd;
+ idata.dst_addr = dst;
+ idata.data = data;
+ idata.data_len = data_len;
+ idata.oui_suffix = oui_suffix;
+ res = hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
+ &idata);
+ if (res == 1)
+ return data_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
+ if (!oui_ctx)
+ return -1;
+
+ return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
+#else /* CONFIG_ETH_P_OUI */
+ return -1;
+#endif /* CONFIG_ETH_P_OUI */
+}
+
+
+static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_channel_info(hapd, ci);
+}
+
+
+#ifdef CONFIG_PASN
+
+static void hostapd_store_ptksa(void *ctx, const u8 *addr,int cipher,
+ u32 life_time, const struct wpa_ptk *ptk)
+{
+ struct hostapd_data *hapd = ctx;
+
+ ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
+ ptk, NULL, NULL, 0);
+}
+
+
+static void hostapd_clear_ptksa(void *ctx, const u8 *addr, int cipher)
+{
+ struct hostapd_data *hapd = ctx;
+
+ ptksa_cache_flush(hapd->ptksa, addr, cipher);
+}
+
+#endif /* CONFIG_PASN */
+
+
+static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id)
+{
+#ifndef CONFIG_NO_VLAN
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return -1;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ struct vlan_description vlan_desc;
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = 1;
+ vlan_desc.untagged = vlan_id;
+ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ wpa_printf(MSG_INFO,
+ "Invalid VLAN ID %d in wpa_psk_file",
+ vlan_id);
+ return -1;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to assign VLAN ID %d from wpa_psk_file to "
+ MACSTR, vlan_id, MAC2STR(sta->addr));
+ return -1;
+ }
+ } else {
+ sta->vlan_id = vlan_id;
+ }
+
+ wpa_printf(MSG_INFO,
+ "Assigned VLAN ID %d from wpa_psk_file to " MACSTR,
+ vlan_id, MAC2STR(sta->addr));
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ ap_sta_bind_vlan(hapd, sta) < 0)
+ return -1;
+#endif /* CONFIG_NO_VLAN */
+
+ return 0;
+}
+
+
+#ifdef CONFIG_OCV
+static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
+ int ap_max_chanwidth, int ap_seg1_idx,
+ int *bandwidth, int *seg1_idx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO,
+ "Failed to get STA info to validate received OCI");
+ return -1;
+ }
+
+ return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth,
+ seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
+ const u8 *data, size_t data_len)
+{
+ struct hostapd_data *hapd = ctx;
+ int res;
+ struct ieee80211_mgmt *m;
+ size_t mlen;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL || sta->wpa_sm == NULL)
+ return -1;
+
+ m = os_zalloc(sizeof(*m) + data_len);
+ if (m == NULL)
+ return -1;
+ mlen = ((u8 *) &m->u - (u8 *) m) + data_len;
+ m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(m->da, dst, ETH_ALEN);
+ os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(&m->u, data, data_len);
+
+ res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0, NULL, 0, 0);
+ os_free(m);
+ return res;
+}
+
+
+static struct wpa_state_machine *
+hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR
+ " based on WPA authenticator callback",
+ MAC2STR(sta_addr));
+ ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT);
+
+ /*
+ * The expected return values from hostapd_add_sta_node() are
+ * 0: successfully added STA entry
+ * -EOPNOTSUPP: driver or driver wrapper does not support/need this
+ * operations
+ * any other negative value: error in adding the STA entry */
+ if (ret < 0 && ret != -EOPNOTSUPP)
+ return NULL;
+
+ sta = ap_sta_add(hapd, sta_addr);
+ if (sta == NULL)
+ return NULL;
+ if (ret == 0)
+ sta->added_unassoc = 1;
+
+ sta->ft_over_ds = 1;
+ if (sta->wpa_sm) {
+ sta->auth_alg = WLAN_AUTH_FT;
+ return sta->wpa_sm;
+ }
+
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ ap_free_sta(hapd, sta);
+ return NULL;
+ }
+ sta->auth_alg = WLAN_AUTH_FT;
+
+ return sta->wpa_sm;
+}
+
+
+static int hostapd_wpa_auth_add_sta_ft(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+ (sta->flags & WLAN_STA_MFP) && ap_sta_is_authorized(sta) &&
+ !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) {
+ /* We could not do this in handle_auth() since there was a
+ * PMF-enabled association for the STA and the new
+ * authentication attempt was not yet fully processed. Now that
+ * we are ready to configure the TK to the driver,
+ * authentication has succeeded and we can clean up the driver
+ * STA entry to avoid issues with any maintained state from the
+ * previous association. */
+ wpa_printf(MSG_DEBUG,
+ "FT: Remove and re-add driver STA entry after successful FT authentication");
+ return ap_sta_re_add(hapd, sta);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ? "+" : "");
+ return -1;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+ /* Configure wpa_group for GTK but ignore error due to driver not
+ * knowing this STA. */
+ ap_sta_bind_vlan(hapd, sta);
+
+ if (sta->vlan_id)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ if (sta->vlan_desc)
+ *vlan = *sta->vlan_desc;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ return 0;
+}
+
+
+static int
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->identity);
+ sta->identity = NULL;
+
+ if (sta->eapol_sm) {
+ os_free(sta->eapol_sm->identity);
+ sta->eapol_sm->identity = NULL;
+ sta->eapol_sm->identity_len = 0;
+ }
+
+ if (!identity_len)
+ return 0;
+
+ /* sta->identity is NULL terminated */
+ sta->identity = os_zalloc(identity_len + 1);
+ if (!sta->identity)
+ return -1;
+ os_memcpy(sta->identity, identity, identity_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->identity = os_zalloc(identity_len);
+ if (!sta->eapol_sm->identity)
+ return -1;
+ os_memcpy(sta->eapol_sm->identity, identity, identity_len);
+ sta->eapol_sm->identity_len = identity_len;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ size_t len;
+ char *identity;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ *buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
+ if (*buf && len)
+ return len;
+
+ if (!sta->identity) {
+ *buf = NULL;
+ return 0;
+ }
+
+ identity = sta->identity;
+ len = os_strlen(identity);
+ *buf = (u8 *) identity;
+
+ return len;
+}
+
+
+static int
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->radius_cui);
+ sta->radius_cui = NULL;
+
+ if (sta->eapol_sm) {
+ wpabuf_free(sta->eapol_sm->radius_cui);
+ sta->eapol_sm->radius_cui = NULL;
+ }
+
+ if (!radius_cui)
+ return 0;
+
+ /* sta->radius_cui is NULL terminated */
+ sta->radius_cui = os_zalloc(radius_cui_len + 1);
+ if (!sta->radius_cui)
+ return -1;
+ os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+ radius_cui_len);
+ if (!sta->eapol_sm->radius_cui)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct wpabuf *b;
+ size_t len;
+ char *radius_cui;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (b) {
+ len = wpabuf_len(b);
+ *buf = wpabuf_head(b);
+ return len;
+ }
+
+ if (!sta->radius_cui) {
+ *buf = NULL;
+ return 0;
+ }
+
+ radius_cui = sta->radius_cui;
+ len = os_strlen(radius_cui);
+ *buf = (u8 *) radius_cui;
+
+ return len;
+}
+
+
+static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
+ int session_timeout)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return;
+
+ if (session_timeout) {
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+ sta->session_timeout_set = 1;
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ } else {
+ sta->session_timeout_set = 0;
+ ap_sta_no_session_timeout(hapd, sta);
+ }
+}
+
+
+static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct os_reltime now, remaining;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->session_timeout_set)
+ return 0;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&sta->session_timeout, &now)) {
+ /* already expired, return >0 as timeout was set */
+ return 1;
+ }
+
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+
+ return (remaining.sec > 0) ? remaining.sec : 1;
+}
+
+
+static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct l2_ethhdr *ethhdr;
+ if (len < sizeof(*ethhdr))
+ return;
+ ethhdr = (struct l2_ethhdr *) buf;
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+ if (!is_multicast_ether_addr(ethhdr->h_dest) &&
+ os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
+ return;
+ wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+ len - sizeof(*ethhdr));
+}
+
+
+static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
+ if (!is_multicast_ether_addr(dst_addr) &&
+ os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+ return;
+ wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
+ len);
+}
+
+
+static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+{
+ struct hostapd_data *hapd = ctx;
+ return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
+}
+
+
+
+static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
+ const char *ft_iface)
+{
+ hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_PULL,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_pull)
+ return -1;
+
+ hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_resp)
+ return -1;
+
+ hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_PUSH,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_push)
+ return -1;
+
+ hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sreq)
+ return -1;
+
+ hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sresp)
+ return -1;
+
+ return 0;
+}
+
+
+static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
+{
+ eth_p_oui_unregister(hapd->oui_pull);
+ hapd->oui_pull = NULL;
+ eth_p_oui_unregister(hapd->oui_resp);
+ hapd->oui_resp = NULL;
+ eth_p_oui_unregister(hapd->oui_push);
+ hapd->oui_push = NULL;
+ eth_p_oui_unregister(hapd->oui_sreq);
+ hapd->oui_sreq = NULL;
+ eth_p_oui_unregister(hapd->oui_sresp);
+ hapd->oui_sresp = NULL;
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_request_radius_psk(void *ctx, const u8 *addr, int key_mgmt,
+ const u8 *anonce,
+ const u8 *eapol, size_t eapol_len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "RADIUS PSK request for " MACSTR " key_mgmt=0x%x",
+ MAC2STR(addr), key_mgmt);
+ wpa_hexdump(MSG_DEBUG, "ANonce", anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAPOL", eapol, eapol_len);
+ hostapd_acl_req_radius_psk(hapd, addr, key_mgmt, anonce, eapol,
+ eapol_len);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+#ifdef CONFIG_PASN
+static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
+ const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr,
+ peer_addr, 0, 0, NULL,
+ ltf_keyseed_len,
+ ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+ struct wpa_auth_ml_rsn_info *info)
+{
+ struct hostapd_data *hapd = ctx;
+ unsigned int i, j;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
+ info->n_mld_links);
+
+ if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+ return -1;
+
+ for (i = 0; i < info->n_mld_links; i++) {
+ unsigned int link_id = info->links[i].link_id;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
+ link_id);
+
+ for (j = 0; j < hapd->iface->interfaces->count; j++) {
+ struct hostapd_iface *iface =
+ hapd->iface->interfaces->iface[j];
+
+ if (!iface->bss[0]->conf->mld_ap ||
+ hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
+ link_id != iface->bss[0]->mld_link_id)
+ continue;
+
+ wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
+ &info->links[i]);
+ break;
+ }
+
+ if (j == hapd->iface->interfaces->count)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+ struct wpa_auth_ml_key_info *info)
+{
+ struct hostapd_data *hapd = ctx;
+ unsigned int i, j;
+
+ wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
+ info->n_mld_links);
+
+ if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+ return -1;
+
+ for (i = 0; i < info->n_mld_links; i++) {
+ u8 link_id = info->links[i].link_id;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: Get link info CB: link_id=%u",
+ link_id);
+
+ for (j = 0; j < hapd->iface->interfaces->count; j++) {
+ struct hostapd_iface *iface =
+ hapd->iface->interfaces->iface[j];
+
+ if (!iface->bss[0]->conf->mld_ap ||
+ hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
+ link_id != iface->bss[0]->mld_link_id)
+ continue;
+
+ wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
+ &info->links[i],
+ info->mgmt_frame_prot,
+ info->beacon_prot);
+ break;
+ }
+
+ if (j == hapd->iface->interfaces->count)
+ wpa_printf(MSG_DEBUG,
+ "WPA_AUTH: MLD: link=%u not found", link_id);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+int hostapd_setup_wpa(struct hostapd_data *hapd)
+{
+ struct wpa_auth_config _conf;
+ static const struct wpa_auth_callbacks cb = {
+ .logger = hostapd_wpa_auth_logger,
+ .disconnect = hostapd_wpa_auth_disconnect,
+ .mic_failure_report = hostapd_wpa_auth_mic_failure_report,
+ .psk_failure_report = hostapd_wpa_auth_psk_failure_report,
+ .set_eapol = hostapd_wpa_auth_set_eapol,
+ .get_eapol = hostapd_wpa_auth_get_eapol,
+ .get_psk = hostapd_wpa_auth_get_psk,
+ .get_msk = hostapd_wpa_auth_get_msk,
+ .set_key = hostapd_wpa_auth_set_key,
+ .get_seqnum = hostapd_wpa_auth_get_seqnum,
+ .send_eapol = hostapd_wpa_auth_send_eapol,
+ .for_each_sta = hostapd_wpa_auth_for_each_sta,
+ .for_each_auth = hostapd_wpa_auth_for_each_auth,
+ .send_ether = hostapd_wpa_auth_send_ether,
+ .send_oui = hostapd_wpa_auth_send_oui,
+ .channel_info = hostapd_channel_info,
+ .update_vlan = hostapd_wpa_auth_update_vlan,
+#ifdef CONFIG_PASN
+ .store_ptksa = hostapd_store_ptksa,
+ .clear_ptksa = hostapd_clear_ptksa,
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_OCV
+ .get_sta_tx_params = hostapd_get_sta_tx_params,
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211R_AP
+ .send_ft_action = hostapd_wpa_auth_send_ft_action,
+ .add_sta = hostapd_wpa_auth_add_sta,
+ .add_sta_ft = hostapd_wpa_auth_add_sta_ft,
+ .add_tspec = hostapd_wpa_auth_add_tspec,
+ .set_vlan = hostapd_wpa_auth_set_vlan,
+ .get_vlan = hostapd_wpa_auth_get_vlan,
+ .set_identity = hostapd_wpa_auth_set_identity,
+ .get_identity = hostapd_wpa_auth_get_identity,
+ .set_radius_cui = hostapd_wpa_auth_set_radius_cui,
+ .get_radius_cui = hostapd_wpa_auth_get_radius_cui,
+ .set_session_timeout = hostapd_wpa_auth_set_session_timeout,
+ .get_session_timeout = hostapd_wpa_auth_get_session_timeout,
+#endif /* CONFIG_IEEE80211R_AP */
+#ifndef CONFIG_NO_RADIUS
+ .request_radius_psk = hostapd_request_radius_psk,
+#endif /* CONFIG_NO_RADIUS */
+#ifdef CONFIG_PASN
+ .set_ltf_keyseed = hostapd_set_ltf_keyseed,
+#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+ .get_ml_rsn_info = hostapd_wpa_auth_get_ml_rsn_info,
+ .get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
+#endif /* CONFIG_IEEE80211BE */
+ };
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+ hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+ _conf.msg_ctx = hapd->msg_ctx;
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+ _conf.tx_status = 1;
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+ _conf.ap_mlme = 1;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) &&
+ (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER ||
+ (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK &&
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "Disable PTK0 rekey support - replaced with disconnect");
+ _conf.wpa_deny_ptk0_rekey = 1;
+ }
+
+ if (_conf.extended_key_id &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
+ else
+ _conf.extended_key_id = 0;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+ _conf.beacon_prot = 0;
+
+#ifdef CONFIG_OCV
+ if (!(hapd->iface->drv_flags2 &
+ (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
+ _conf.ocv = 0;
+#endif /* CONFIG_OCV */
+
+ _conf.secure_ltf =
+ !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
+ _conf.secure_rtt =
+ !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
+ _conf.prot_range_neg =
+ !!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+
+ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
+ if (hapd->wpa_auth == NULL) {
+ wpa_printf(MSG_ERROR, "WPA initialization failed.");
+ return -1;
+ }
+
+ if (hostapd_set_privacy(hapd, 1)) {
+ wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked "
+ "for interface %s", hapd->conf->iface);
+ return -1;
+ }
+
+ wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+ if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) {
+ wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+ "the kernel driver.");
+ return -1;
+ }
+
+ if (rsn_preauth_iface_init(hapd)) {
+ wpa_printf(MSG_ERROR, "Initialization of RSN "
+ "pre-authentication failed.");
+ return -1;
+ }
+
+ if (!hapd->ptksa)
+ hapd->ptksa = ptksa_cache_init();
+ if (!hapd->ptksa) {
+ wpa_printf(MSG_ERROR, "Failed to allocate PTKSA cache");
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (!hostapd_drv_none(hapd) &&
+ wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+ const char *ft_iface;
+
+ if (hapd->conf->ft_iface[0])
+ ft_iface = hapd->conf->ft_iface;
+ else if (hapd->conf->bridge[0])
+ ft_iface = hapd->conf->bridge;
+ else
+ ft_iface = hapd->conf->iface;
+ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+ hostapd_rrb_receive, hapd, 1);
+ if (!hapd->l2) {
+ wpa_printf(MSG_ERROR, "Failed to open l2_packet "
+ "interface");
+ return -1;
+ }
+
+ if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to open ETH_P_OUI interface");
+ return -1;
+ }
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ return 0;
+
+}
+
+
+void hostapd_reconfig_wpa(struct hostapd_data *hapd)
+{
+ struct wpa_auth_config wpa_auth_conf;
+ hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
+ wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+}
+
+
+void hostapd_deinit_wpa(struct hostapd_data *hapd)
+{
+ ieee80211_tkip_countermeasures_deinit(hapd);
+ ptksa_cache_deinit(hapd->ptksa);
+ hapd->ptksa = NULL;
+
+ rsn_preauth_iface_deinit(hapd);
+ if (hapd->wpa_auth) {
+ wpa_deinit(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+
+ if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) {
+ wpa_printf(MSG_DEBUG, "Could not disable "
+ "PrivacyInvoked for interface %s",
+ hapd->conf->iface);
+ }
+
+ if (hapd->drv_priv &&
+ hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+ wpa_printf(MSG_DEBUG, "Could not remove generic "
+ "information element from interface %s",
+ hapd->conf->iface);
+ }
+ }
+ ieee802_1x_deinit(hapd);
+
+#ifdef CONFIG_IEEE80211R_AP
+ eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
+ hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
+ eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
+ hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
+ l2_packet_deinit(hapd->l2);
+ hapd->l2 = NULL;
+ hostapd_wpa_unregister_ft_oui(hapd);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ forced_memzero(hapd->last_gtk, WPA_GTK_MAX_LEN);
+ forced_memzero(hapd->last_igtk, WPA_IGTK_MAX_LEN);
+ forced_memzero(hapd->last_bigtk, WPA_BIGTK_MAX_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.h
new file mode 100644
index 0000000..1b13ae7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.h
@@ -0,0 +1,16 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_GLUE_H
+#define WPA_AUTH_GLUE_H
+
+int hostapd_setup_wpa(struct hostapd_data *hapd);
+void hostapd_reconfig_wpa(struct hostapd_data *hapd);
+void hostapd_deinit_wpa(struct hostapd_data *hapd);
+
+#endif /* WPA_AUTH_GLUE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_i.h
new file mode 100644
index 0000000..74ae5ad
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_i.h
@@ -0,0 +1,341 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_I_H
+#define WPA_AUTH_I_H
+
+#include "utils/list.h"
+
+/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+#define RSNA_MAX_EAPOL_RETRIES 4
+
+struct wpa_group;
+
+struct wpa_state_machine {
+ struct wpa_authenticator *wpa_auth;
+ struct wpa_group *group;
+
+ u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
+ u16 auth_alg;
+
+ enum {
+ WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+ WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
+ WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
+ WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
+ WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
+ } wpa_ptk_state;
+
+ enum {
+ WPA_PTK_GROUP_IDLE = 0,
+ WPA_PTK_GROUP_REKEYNEGOTIATING,
+ WPA_PTK_GROUP_REKEYESTABLISHED,
+ WPA_PTK_GROUP_KEYERROR
+ } wpa_ptk_group_state;
+
+ bool Init;
+ bool DeauthenticationRequest;
+ bool AuthenticationRequest;
+ bool ReAuthenticationRequest;
+ bool Disconnect;
+ u16 disconnect_reason; /* specific reason code to use with Disconnect */
+ u32 TimeoutCtr;
+ u32 GTimeoutCtr;
+ bool TimeoutEvt;
+ bool EAPOLKeyReceived;
+ bool EAPOLKeyPairwise;
+ bool EAPOLKeyRequest;
+ bool MICVerified;
+ bool GUpdateStationKeys;
+ u8 ANonce[WPA_NONCE_LEN];
+ u8 SNonce[WPA_NONCE_LEN];
+ u8 alt_SNonce[WPA_NONCE_LEN];
+ u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ u8 PMK[PMK_LEN_MAX];
+ unsigned int pmk_len;
+ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
+ struct wpa_ptk PTK;
+ u8 keyidx_active;
+ bool use_ext_key_id;
+ bool PTK_valid;
+ bool pairwise_set;
+ bool tk_already_set;
+ int keycount;
+ bool Pair;
+ struct wpa_key_replay_counter {
+ u8 counter[WPA_REPLAY_COUNTER_LEN];
+ bool valid;
+ } key_replay[RSNA_MAX_EAPOL_RETRIES],
+ prev_key_replay[RSNA_MAX_EAPOL_RETRIES];
+ bool PInitAKeys; /* WPA only, not in IEEE 802.11i */
+ bool PTKRequest; /* not in IEEE 802.11i state machine */
+ bool has_GTK;
+ bool PtkGroupInit; /* init request for PTK Group state machine */
+
+ u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
+ size_t last_rx_eapol_key_len;
+
+ unsigned int changed:1;
+ unsigned int in_step_loop:1;
+ unsigned int pending_deinit:1;
+ unsigned int started:1;
+ unsigned int mgmt_frame_prot:1;
+ unsigned int mfpr:1;
+ unsigned int rx_eapol_key_secure:1;
+ unsigned int update_snonce:1;
+ unsigned int alt_snonce_valid:1;
+ unsigned int waiting_radius_psk:1;
+#ifdef CONFIG_IEEE80211R_AP
+ unsigned int ft_completed:1;
+ unsigned int pmk_r1_name_valid:1;
+#endif /* CONFIG_IEEE80211R_AP */
+ unsigned int is_wnmsleep:1;
+ unsigned int pmkid_set:1;
+
+ unsigned int ptkstart_without_success;
+
+#ifdef CONFIG_OCV
+ int ocv_enabled;
+#endif /* CONFIG_OCV */
+
+ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int req_replay_counter_used;
+
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
+ u8 *rsnxe;
+ size_t rsnxe_len;
+
+ enum {
+ WPA_VERSION_NO_WPA = 0 /* WPA not used */,
+ WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
+ WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
+ } wpa;
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+ int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+ u32 dot11RSNAStatsTKIPRemoteMICFailures;
+
+#ifdef CONFIG_IEEE80211R_AP
+ u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+ * first 384 bits of MSK */
+ size_t xxkey_len;
+ u8 pmk_r1[PMK_LEN_MAX];
+ unsigned int pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
+ * Request */
+ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
+ size_t r0kh_id_len;
+ u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
+ * message 2/4 */
+ u8 *assoc_resp_ftie;
+
+ void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len);
+ void *ft_pending_cb_ctx;
+ struct wpabuf *ft_pending_req_ies;
+ u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
+ u8 ft_pending_auth_transaction;
+ u8 ft_pending_current_ap[ETH_ALEN];
+ int ft_pending_pull_left_retries;
+#endif /* CONFIG_IEEE80211R_AP */
+
+ int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+ u8 ip_addr[4];
+ unsigned int ip_addr_bit;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_FILS
+ u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+ u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+ size_t fils_key_auth_len;
+ unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_DPP2
+ struct wpabuf *dpp_z;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ void (*eapol_status_cb)(void *ctx1, void *ctx2);
+ void *eapol_status_cb_ctx1;
+ void *eapol_status_cb_ctx2;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+ u8 own_mld_addr[ETH_ALEN];
+ u8 peer_mld_addr[ETH_ALEN];
+ s8 mld_assoc_link_id;
+ u8 n_mld_affiliated_links;
+
+ struct mld_link {
+ bool valid;
+ u8 peer_addr[ETH_ALEN];
+ u8 own_addr[ETH_ALEN];
+
+ const u8 *rsne;
+ size_t rsne_len;
+ const u8 *rsnxe;
+ size_t rsnxe_len;
+ } mld_links[MAX_NUM_MLD_LINKS];
+#endif /* CONFIG_IEEE80211BE */
+};
+
+
+/* per group key state machine data */
+struct wpa_group {
+ struct wpa_group *next;
+ int vlan_id;
+
+ bool GInit;
+ int GKeyDoneStations;
+ bool GTKReKey;
+ int GTK_len;
+ int GN, GM;
+ bool GTKAuthenticator;
+ u8 Counter[WPA_NONCE_LEN];
+
+ enum {
+ WPA_GROUP_GTK_INIT = 0,
+ WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+ WPA_GROUP_FATAL_FAILURE
+ } wpa_group_state;
+
+ u8 GMK[WPA_GMK_LEN];
+ u8 GTK[2][WPA_GTK_MAX_LEN];
+ u8 GNonce[WPA_NONCE_LEN];
+ bool changed;
+ bool first_sta_seen;
+ bool reject_4way_hs_for_entropy;
+ u8 IGTK[2][WPA_IGTK_MAX_LEN];
+ u8 BIGTK[2][WPA_IGTK_MAX_LEN];
+ int GN_igtk, GM_igtk;
+ int GN_bigtk, GM_bigtk;
+ /* Number of references except those in struct wpa_group->next */
+ unsigned int references;
+ unsigned int num_setup_iface;
+};
+
+
+struct wpa_ft_pmk_cache;
+
+/* per authenticator data */
+struct wpa_authenticator {
+ struct wpa_group *group;
+
+ unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
+ u32 dot11RSNAAuthenticationSuiteSelected;
+ u32 dot11RSNAPairwiseCipherSelected;
+ u32 dot11RSNAGroupCipherSelected;
+ u8 dot11RSNAPMKIDUsed[PMKID_LEN];
+ u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */
+ u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */
+ u32 dot11RSNAGroupCipherRequested; /* FIX: update */
+ unsigned int dot11RSNATKIPCounterMeasuresInvoked;
+ unsigned int dot11RSNA4WayHandshakeFailures;
+
+ struct wpa_auth_config conf;
+ const struct wpa_auth_callbacks *cb;
+ void *cb_ctx;
+
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+ u8 addr[ETH_ALEN];
+
+ struct rsn_pmksa_cache *pmksa;
+ struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+ struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
+};
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+ u32 dom;
+ struct os_reltime time_offset; /* local time - offset = remote time */
+
+ /* accepted sequence numbers: (offset ... offset + 0x40000000]
+ * (except those in last)
+ * dropped sequence numbers: (offset - 0x40000000 ... offset]
+ * all others trigger SEQ_REQ message (except first message)
+ */
+ u32 last[FT_REMOTE_SEQ_BACKLOG];
+ unsigned int num_last;
+ u32 offsetidx;
+
+ struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+ u32 dom; /* non zero if initialized */
+ u32 seq;
+};
+
+struct ft_remote_seq {
+ struct ft_remote_seq_rx rx;
+ struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+ const u8 *pmkid);
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len);
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *txt);
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *fmt, ...)
+ PRINTF_FORMAT(4, 5);
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int key_info,
+ const u8 *key_rsc, const u8 *nonce,
+ const u8 *kde, size_t kde_len,
+ int keyidx, int encr, int force_version);
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_state_machine *sm, void *ctx),
+ void *cb_ctx);
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_authenticator *a, void *ctx),
+ void *cb_ctx);
+void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int cipher,
+ u32 life_time, const struct wpa_ptk *ptk);
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *anonce, const u8 *snonce,
+ u8 *buf, size_t len, const u8 *subelem,
+ size_t subelem_len, int rsnxe_used);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+ size_t *key_len, size_t kdk_len);
+void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
+ const u8 *pmk_r1, const u8 *pmk_r0_name,
+ size_t key_len);
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
+void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry);
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
+ const u8 *pmk_r0_name);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#endif /* WPA_AUTH_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.c
new file mode 100644
index 0000000..43ccec9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.c
@@ -0,0 +1,1253 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_ie.h"
+#include "wpa_auth_i.h"
+
+
+#ifdef CONFIG_RSN_TESTING
+int rsn_testing = 0;
+#endif /* CONFIG_RSN_TESTING */
+
+
+static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ struct wpa_ie_hdr *hdr;
+ int num_suites;
+ u8 *pos, *count;
+ u32 suite;
+
+ hdr = (struct wpa_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+ RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+ WPA_PUT_LE16(hdr->version, WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
+ if (suite == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+ conf->wpa_group);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += WPA_SELECTOR_LEN;
+
+ count = pos;
+ pos += 2;
+
+ num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
+ if (num_suites == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+ conf->wpa_pairwise);
+ return -1;
+ }
+ pos += num_suites * WPA_SELECTOR_LEN;
+ WPA_PUT_LE16(count, num_suites);
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+ conf->wpa_key_mgmt);
+ return -1;
+ }
+ WPA_PUT_LE16(count, num_suites);
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
+{
+ u16 capab = 0;
+
+ if (conf->rsn_preauth)
+ capab |= WPA_CAPABILITY_PREAUTH;
+ if (conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#ifdef CONFIG_OCV
+ if (conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing)
+ capab |= BIT(8) | BIT(15);
+#endif /* CONFIG_RSN_TESTING */
+ if (conf->extended_key_id)
+ capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
+
+ return capab;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+ const u8 *pmkid)
+{
+ struct rsn_ie_hdr *hdr;
+ int num_suites, res;
+ u8 *pos, *count;
+ u32 suite;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+ if (suite == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+ conf->wpa_group);
+ return -1;
+ }
+ RSN_SELECTOR_PUT(pos, suite);
+ pos += RSN_SELECTOR_LEN;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
+ res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+ num_suites += res;
+ pos += res * RSN_SELECTOR_LEN;
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
+ if (num_suites == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+ conf->rsn_pairwise);
+ return -1;
+ }
+ WPA_PUT_LE16(count, num_suites);
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_SHA384
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SHA384 */
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_SAE
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SAE */
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_FILS
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_PASN
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
+ if (num_suites == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+ conf->wpa_key_mgmt);
+ return -1;
+ }
+ WPA_PUT_LE16(count, num_suites);
+
+ /* RSN Capabilities */
+ WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ pos += 2;
+
+ if (pmkid) {
+ if (2 + PMKID_LEN > buf + len - pos)
+ return -1;
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ os_memcpy(pos, pmkid, PMKID_LEN);
+ pos += PMKID_LEN;
+ }
+
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+ if (2 + 4 > buf + len - pos)
+ return -1;
+ if (pmkid == NULL) {
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ }
+
+ /* Management Group Cipher Suite */
+ switch (conf->group_mgmt_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Invalid group management cipher (0x%x)",
+ conf->group_mgmt_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+ }
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ /*
+ * Fill in any defined fields and add extra data to the end of
+ * the element.
+ */
+ int pmkid_count_set = pmkid != NULL;
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+ pmkid_count_set = 1;
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+
+ os_memset(pos, 0x12, 17);
+ pos += 17;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
+ hdr->len = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ u8 *pos = buf;
+ u16 capab = 0;
+ size_t flen;
+
+ if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
+ (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+ conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
+ wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+ if (conf->sae_pk)
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+ }
+
+ if (conf->secure_ltf)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+ if (conf->secure_rtt)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+ if (conf->prot_range_neg)
+ capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+
+ flen = (capab & 0xff00) ? 2 : 1;
+ if (!capab)
+ return 0; /* no supported extended RSN capabilities */
+ if (len < 2 + flen)
+ return -1;
+ capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = flen;
+ *pos++ = capab & 0x00ff;
+ capab >>= 8;
+ if (capab)
+ *pos++ = capab;
+
+ return pos - buf;
+}
+
+
+static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ if (conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#ifdef CONFIG_OCV
+ if (conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
+{
+ u8 *pos, buf[128];
+ int res;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_auth->conf.own_ie_override_len) {
+ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+ wpa_auth->conf.own_ie_override,
+ wpa_auth->conf.own_ie_override_len);
+ os_free(wpa_auth->wpa_ie);
+ wpa_auth->wpa_ie =
+ os_malloc(wpa_auth->conf.own_ie_override_len);
+ if (wpa_auth->wpa_ie == NULL)
+ return -1;
+ os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
+ wpa_auth->conf.own_ie_override_len);
+ wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pos = buf;
+
+ if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
+ pos = wpa_write_osen(&wpa_auth->conf, pos);
+ }
+ if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
+ res = wpa_write_rsn_ie(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos, NULL);
+ if (res < 0)
+ return res;
+ pos += res;
+ res = wpa_write_rsnxe(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
+ res = wpa_write_mdie(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
+ res = wpa_write_wpa_ie(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+
+ os_free(wpa_auth->wpa_ie);
+ wpa_auth->wpa_ie = os_malloc(pos - buf);
+ if (wpa_auth->wpa_ie == NULL)
+ return -1;
+ os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
+ wpa_auth->wpa_ie_len = pos - buf;
+
+ return 0;
+}
+
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len)
+{
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + data_len + data2_len;
+ RSN_SELECTOR_PUT(pos, kde);
+ pos += RSN_SELECTOR_LEN;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+ if (data2) {
+ os_memcpy(pos, data2, data2_len);
+ pos += data2_len;
+ }
+ return pos;
+}
+
+
+struct wpa_auth_okc_iter_data {
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *aa;
+ const u8 *spa;
+ const u8 *pmkid;
+};
+
+
+static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
+{
+ struct wpa_auth_okc_iter_data *data = ctx;
+ data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
+ data->pmkid);
+ if (data->pmksa)
+ return 1;
+ return 0;
+}
+
+
+enum wpa_validate_result
+wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int freq,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsnxe, size_t rsnxe_len,
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ struct wpa_ie_data data;
+ int ciphers, key_mgmt, res, version;
+ u32 selector;
+ size_t i;
+ const u8 *pmkid = NULL;
+
+ if (wpa_auth == NULL || sm == NULL)
+ return WPA_NOT_ENABLED;
+
+ if (wpa_ie == NULL || wpa_ie_len < 1)
+ return WPA_INVALID_IE;
+
+ if (wpa_ie[0] == WLAN_EID_RSN)
+ version = WPA_PROTO_RSN;
+ else
+ version = WPA_PROTO_WPA;
+
+ if (!(wpa_auth->conf.wpa & version)) {
+ wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR,
+ version, MAC2STR(sm->addr));
+ return WPA_INVALID_PROTO;
+ }
+
+ if (version == WPA_PROTO_RSN) {
+ res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+ if (!data.has_pairwise)
+ data.pairwise_cipher = wpa_default_rsn_cipher(freq);
+ if (!data.has_group)
+ data.group_cipher = wpa_default_rsn_cipher(freq);
+
+ if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie &&
+ !wpa_key_mgmt_only_ft(data.key_mgmt)) {
+ /* Workaround for some HP and Epson printers that seem
+ * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP
+ * advertised RSNE to Association Request frame. */
+ wpa_printf(MSG_DEBUG,
+ "RSN: FT set in RSNE AKM but MDE is missing from "
+ MACSTR
+ " - ignore FT AKM(s) because there's also a non-FT AKM",
+ MAC2STR(sm->addr));
+ data.key_mgmt &= ~WPA_KEY_MGMT_FT;
+ }
+
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (0) {
+ }
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#ifdef CONFIG_SAE
+ else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
+ selector = RSN_AUTH_KEY_MGMT_SAE;
+ else if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+ selector = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
+ selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+ selector = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
+#endif /* CONFIG_SAE */
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+#ifdef CONFIG_OWE
+ else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
+ selector = RSN_AUTH_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
+ selector = RSN_AUTH_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
+ selector = RSN_AUTH_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
+ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+ selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ data.pairwise_cipher);
+ if (!selector)
+ selector = RSN_CIPHER_SUITE_CCMP;
+ wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+ selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ data.group_cipher);
+ if (!selector)
+ selector = RSN_CIPHER_SUITE_CCMP;
+ wpa_auth->dot11RSNAGroupCipherSelected = selector;
+ } else {
+ res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
+
+ selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+ selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+ data.pairwise_cipher);
+ if (!selector)
+ selector = RSN_CIPHER_SUITE_TKIP;
+ wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+ selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+ data.group_cipher);
+ if (!selector)
+ selector = WPA_CIPHER_SUITE_TKIP;
+ wpa_auth->dot11RSNAGroupCipherSelected = selector;
+ }
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from "
+ MACSTR " (res=%d)", MAC2STR(sm->addr), res);
+ wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len);
+ return WPA_INVALID_IE;
+ }
+
+ if (data.group_cipher != wpa_auth->conf.wpa_group) {
+ wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from "
+ MACSTR, data.group_cipher, MAC2STR(sm->addr));
+ return WPA_INVALID_GROUP;
+ }
+
+ key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+ if (!key_mgmt) {
+ wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
+ MACSTR, data.key_mgmt, MAC2STR(sm->addr));
+ return WPA_INVALID_AKMP;
+ }
+ if (0) {
+ }
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+ else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+#ifdef CONFIG_SAE
+ else if (key_mgmt & WPA_KEY_MGMT_SAE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+ else if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+#endif /* CONFIG_SAE */
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_OWE
+ else if (key_mgmt & WPA_KEY_MGMT_OWE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (key_mgmt & WPA_KEY_MGMT_DPP)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (key_mgmt & WPA_KEY_MGMT_OSEN)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
+ else
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+ if (version == WPA_PROTO_RSN)
+ ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+ else
+ ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
+ if (!ciphers) {
+ wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
+ "from " MACSTR,
+ version == WPA_PROTO_RSN ? "RSN" : "WPA",
+ data.pairwise_cipher, MAC2STR(sm->addr));
+ return WPA_INVALID_PAIRWISE;
+ }
+
+ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+ if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG, "Management frame protection "
+ "required, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+
+ if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+ {
+ wpa_printf(MSG_DEBUG, "Unsupported management group "
+ "cipher %d", data.mgmt_group_cipher);
+ return WPA_INVALID_MGMT_GROUP_CIPHER;
+ }
+ }
+
+#ifdef CONFIG_SAE
+ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ wpa_auth->conf.sae_require_mfp &&
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with SAE, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_OCV
+ if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ /* Some legacy MFP incapable STAs wrongly copy OCVC bit from
+ * AP RSN capabilities. To improve interoperability with such
+ * legacy STAs allow connection without enabling OCV when the
+ * workaround mode (ocv=2) is enabled.
+ */
+ if (wpa_auth->conf.ocv == 2) {
+ wpa_printf(MSG_DEBUG,
+ "Allow connecting MFP incapable and OCV capable STA without enabling OCV");
+ wpa_auth_set_ocv(sm, 0);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with OCV, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+ } else {
+ wpa_auth_set_ocv(sm, (data.capabilities & WPA_CAPABILITY_OCVC) ?
+ wpa_auth->conf.ocv : 0);
+ }
+#endif /* CONFIG_OCV */
+
+ if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+ !(data.capabilities & WPA_CAPABILITY_MFPC))
+ sm->mgmt_frame_prot = 0;
+ else
+ sm->mgmt_frame_prot = 1;
+ sm->mfpr = !!(data.capabilities & WPA_CAPABILITY_MFPR);
+
+ if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection cannot use TKIP");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
+ "MDIE not included");
+ return WPA_INVALID_MDIE;
+ }
+ if (os_memcmp(mdie, wpa_auth->conf.mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown "
+ "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
+ return WPA_INVALID_MDIE;
+ }
+ } else if (mdie != NULL) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Trying to use non-FT AKM suite, but MDIE included");
+ return WPA_INVALID_AKMP;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_OWE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: No Diffie-Hellman Parameter element");
+ return WPA_INVALID_AKMP;
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+ ((conf->dpp_pfs == 1 && !owe_dh) ||
+ (conf->dpp_pfs == 2 && owe_dh))) {
+ wpa_printf(MSG_DEBUG, "DPP: PFS %s",
+ conf->dpp_pfs == 1 ? "required" : "not allowed");
+ return WPA_DENIED_OTHER_REASON;
+ }
+#endif /* CONFIG_DPP2 */
+
+ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+ if (sm->pairwise < 0)
+ return WPA_INVALID_PAIRWISE;
+
+ /* TODO: clear WPA/WPA2 state if STA changes from one to another */
+ if (wpa_ie[0] == WLAN_EID_RSN)
+ sm->wpa = WPA_VERSION_WPA2;
+ else
+ sm->wpa = WPA_VERSION_WPA;
+
+#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
+ if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
+ (sm->auth_alg == WLAN_AUTH_FILS_SK ||
+ sm->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sm->auth_alg == WLAN_AUTH_FILS_PK) &&
+ (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid ||
+ os_memcmp_const(data.pmkid, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0)) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKR1Name match for FILS+FT");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */
+
+ sm->pmksa = NULL;
+ for (i = 0; i < data.num_pmkid; i++) {
+ wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
+ &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+ sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+ &data.pmkid[i * PMKID_LEN]);
+ if (sm->pmksa) {
+ pmkid = sm->pmksa->pmkid;
+ break;
+ }
+ }
+ for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
+ i < data.num_pmkid; i++) {
+ struct wpa_auth_okc_iter_data idata;
+ idata.pmksa = NULL;
+ idata.aa = wpa_auth->addr;
+ idata.spa = sm->addr;
+ idata.pmkid = &data.pmkid[i * PMKID_LEN];
+ wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
+ if (idata.pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "OKC match for PMKID");
+ sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
+ idata.pmksa,
+ wpa_auth->addr,
+ idata.pmkid);
+ pmkid = idata.pmkid;
+ break;
+ }
+ }
+ if (sm->pmksa && pmkid) {
+ struct vlan_description *vlan;
+
+ vlan = sm->pmksa->vlan_desc;
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PMKID found from PMKSA cache eap_type=%d vlan=%d%s",
+ sm->pmksa->eap_type_authsrv,
+ vlan ? vlan->untagged : 0,
+ (vlan && vlan->tagged[0]) ? "+" : "");
+ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
+ }
+
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
+ !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for SAE");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_DPP
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for DPP");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_DPP */
+
+ if (conf->extended_key_id && sm->wpa == WPA_VERSION_WPA2 &&
+ sm->pairwise != WPA_CIPHER_TKIP &&
+ (data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
+ sm->use_ext_key_id = true;
+ if (conf->extended_key_id == 2 &&
+ !wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+ !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+ sm->keyidx_active = 1;
+ else
+ sm->keyidx_active = 0;
+ wpa_printf(MSG_DEBUG,
+ "RSN: Extended Key ID supported (start with %d)",
+ sm->keyidx_active);
+ } else {
+ sm->use_ext_key_id = false;
+ }
+
+ if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
+ os_free(sm->wpa_ie);
+ sm->wpa_ie = os_malloc(wpa_ie_len);
+ if (sm->wpa_ie == NULL)
+ return WPA_ALLOC_FAIL;
+ }
+ os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
+ sm->wpa_ie_len = wpa_ie_len;
+
+ if (rsnxe && rsnxe_len) {
+ if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) {
+ os_free(sm->rsnxe);
+ sm->rsnxe = os_malloc(rsnxe_len);
+ if (!sm->rsnxe)
+ return WPA_ALLOC_FAIL;
+ }
+ os_memcpy(sm->rsnxe, rsnxe, rsnxe_len);
+ sm->rsnxe_len = rsnxe_len;
+ } else {
+ os_free(sm->rsnxe);
+ sm->rsnxe = NULL;
+ sm->rsnxe_len = 0;
+ }
+
+ return WPA_IE_OK;
+}
+
+
+#ifdef CONFIG_HS20
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len)
+{
+ if (wpa_auth == NULL || sm == NULL)
+ return -1;
+
+ /* TODO: parse OSEN element */
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+ sm->mgmt_frame_prot = 1;
+ sm->pairwise = WPA_CIPHER_CCMP;
+ sm->wpa = WPA_VERSION_WPA2;
+
+ if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
+ os_free(sm->wpa_ie);
+ sm->wpa_ie = os_malloc(osen_ie_len);
+ if (sm->wpa_ie == NULL)
+ return -1;
+ }
+
+ os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
+ sm->wpa_ie_len = osen_ie_len;
+
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
+{
+ return sm ? sm->mgmt_frame_prot : 0;
+}
+
+
+#ifdef CONFIG_OCV
+
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
+{
+ if (sm)
+ sm->ocv_enabled = ocv;
+}
+
+
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
+{
+ return sm ? sm->ocv_enabled : 0;
+}
+
+#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_OWE
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len,
+ const u8 *req_ies, size_t req_ies_len)
+{
+ int res;
+ struct wpa_auth_config *conf;
+
+ if (!sm)
+ return pos;
+ conf = &sm->wpa_auth->conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->own_ie_override_len) {
+ if (max_len < conf->own_ie_override_len)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+ conf->own_ie_override, conf->own_ie_override_len);
+ os_memcpy(pos, conf->own_ie_override,
+ conf->own_ie_override_len);
+ return pos + conf->own_ie_override_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ res = wpa_write_rsn_ie(conf, pos, max_len,
+ sm->pmksa ? sm->pmksa->pmkid : NULL);
+ if (res < 0)
+ return pos;
+ return pos + res;
+}
+#endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+ u8 *pos, size_t max_len,
+ const u8 *req_ies, size_t req_ies_len)
+{
+ int res;
+
+ if (!sm ||
+ sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384))
+ return pos;
+
+ res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL);
+ if (res < 0)
+ return pos;
+ return pos + res;
+}
+
+
+bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
+ u8 *fd_rsn_info)
+{
+ struct wpa_auth_config *conf;
+ u32 selectors = 0;
+ u8 *pos = fd_rsn_info;
+ int i, res;
+ u32 cipher, suite, selector, mask;
+ u8 tmp[10 * RSN_SELECTOR_LEN];
+
+ if (!wpa_auth)
+ return false;
+ conf = &wpa_auth->conf;
+
+ if (!(conf->wpa & WPA_PROTO_RSN))
+ return false;
+
+ /* RSN Capability (B0..B15) */
+ WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ pos += 2;
+
+ /* Group Data Cipher Suite Selector (B16..B21) */
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+ if (suite == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+ cipher = 63; /* No cipher suite selected */
+ else if ((suite >> 8) == 0x000fac && ((suite & 0xff) <= 13))
+ cipher = suite & 0xff;
+ else
+ cipher = 62; /* vendor specific */
+ selectors |= cipher;
+
+ /* Group Management Cipher Suite Selector (B22..B27) */
+ cipher = 63; /* Default to no cipher suite selected */
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ switch (conf->group_mgmt_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
+ cipher = RSN_CIPHER_SUITE_AES_128_CMAC & 0xff;
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ cipher = RSN_CIPHER_SUITE_BIP_GMAC_128 & 0xff;
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ cipher = RSN_CIPHER_SUITE_BIP_GMAC_256 & 0xff;
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ cipher = RSN_CIPHER_SUITE_BIP_CMAC_256 & 0xff;
+ break;
+ }
+ }
+ selectors |= cipher << 6;
+
+ /* Pairwise Cipher Suite Selector (B28..B33) */
+ cipher = 63; /* Default to no cipher suite selected */
+ res = rsn_cipher_put_suites(tmp, conf->rsn_pairwise);
+ if (res == 1 && tmp[0] == 0x00 && tmp[1] == 0x0f && tmp[2] == 0xac &&
+ tmp[3] <= 13)
+ cipher = tmp[3];
+ selectors |= cipher << 12;
+
+ /* AKM Suite Selector (B34..B39) */
+ selector = 0; /* default to AKM from RSNE in Beacon/Probe Response */
+ mask = WPA_KEY_MGMT_FILS_SHA256 | WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA384;
+ if ((conf->wpa_key_mgmt & mask) && (conf->wpa_key_mgmt & ~mask) == 0) {
+ suite = conf->wpa_key_mgmt & mask;
+ if (suite == WPA_KEY_MGMT_FILS_SHA256)
+ selector = 1; /* 00-0f-ac:14 */
+ else if (suite == WPA_KEY_MGMT_FILS_SHA384)
+ selector = 2; /* 00-0f-ac:15 */
+ else if (suite == (WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FILS_SHA384))
+ selector = 3; /* 00-0f-ac:14 or 00-0f-ac:15 */
+ else if (suite == WPA_KEY_MGMT_FT_FILS_SHA384)
+ selector = 4; /* 00-0f-ac:17 */
+ }
+ selectors |= selector << 18;
+
+ for (i = 0; i < 3; i++) {
+ *pos++ = selectors & 0xff;
+ selectors >>= 8;
+ }
+
+ return true;
+}
+
+#endif /* CONFIG_FILS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.h
new file mode 100644
index 0000000..dd44b9e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.h
@@ -0,0 +1,16 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_IE_H
+#define WPA_AUTH_IE_H
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len);
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_IE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.c
new file mode 100644
index 0000000..625f405
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.c
@@ -0,0 +1,496 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "wpa_auth_kay.h"
+#include "ieee802_1x.h"
+
+
+#define DEFAULT_KEY_LEN 16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN 16
+
+
+static int hapd_macsec_init(void *priv, struct macsec_init_params *params)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->macsec_init)
+ return -1;
+ return hapd->driver->macsec_init(hapd->drv_priv, params);
+}
+
+
+static int hapd_macsec_deinit(void *priv)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->macsec_deinit)
+ return -1;
+ return hapd->driver->macsec_deinit(hapd->drv_priv);
+}
+
+
+static int hapd_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->macsec_get_capability)
+ return -1;
+ return hapd->driver->macsec_get_capability(hapd->drv_priv, cap);
+}
+
+
+static int hapd_enable_protect_frames(void *priv, bool enabled)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->enable_protect_frames)
+ return -1;
+ return hapd->driver->enable_protect_frames(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_enable_encrypt(void *priv, bool enabled)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->enable_encrypt)
+ return -1;
+ return hapd->driver->enable_encrypt(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_set_replay_protect(void *priv, bool enabled, u32 window)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->set_replay_protect)
+ return -1;
+ return hapd->driver->set_replay_protect(hapd->drv_priv, enabled,
+ window);
+}
+
+
+static int hapd_set_current_cipher_suite(void *priv, u64 cs)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->set_current_cipher_suite)
+ return -1;
+ return hapd->driver->set_current_cipher_suite(hapd->drv_priv, cs);
+}
+
+
+static int hapd_enable_controlled_port(void *priv, bool enabled)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->enable_controlled_port)
+ return -1;
+ return hapd->driver->enable_controlled_port(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->get_receive_lowest_pn)
+ return -1;
+ return hapd->driver->get_receive_lowest_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->get_transmit_next_pn)
+ return -1;
+ return hapd->driver->get_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->set_transmit_next_pn)
+ return -1;
+ return hapd->driver->set_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static unsigned int conf_offset_val(enum confidentiality_offset co)
+{
+ switch (co) {
+ case CONFIDENTIALITY_OFFSET_30:
+ return 30;
+ case CONFIDENTIALITY_OFFSET_50:
+ return 50;
+ default:
+ return 0;
+ }
+}
+
+
+static int hapd_create_receive_sc(void *priv, struct receive_sc *sc,
+ enum validate_frames vf,
+ enum confidentiality_offset co)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->create_receive_sc)
+ return -1;
+ return hapd->driver->create_receive_sc(hapd->drv_priv, sc,
+ conf_offset_val(co), vf);
+}
+
+
+static int hapd_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->delete_receive_sc)
+ return -1;
+ return hapd->driver->delete_receive_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->create_receive_sa)
+ return -1;
+ return hapd->driver->create_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->delete_receive_sa)
+ return -1;
+ return hapd->driver->delete_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->enable_receive_sa)
+ return -1;
+ return hapd->driver->enable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->disable_receive_sa)
+ return -1;
+ return hapd->driver->disable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int
+hapd_create_transmit_sc(void *priv, struct transmit_sc *sc,
+ enum confidentiality_offset co)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->create_transmit_sc)
+ return -1;
+ return hapd->driver->create_transmit_sc(hapd->drv_priv, sc,
+ conf_offset_val(co));
+}
+
+
+static int hapd_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->delete_transmit_sc)
+ return -1;
+ return hapd->driver->delete_transmit_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->create_transmit_sa)
+ return -1;
+ return hapd->driver->create_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->delete_transmit_sa)
+ return -1;
+ return hapd->driver->delete_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->enable_transmit_sa)
+ return -1;
+ return hapd->driver->enable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct hostapd_data *hapd = priv;
+
+ if (!hapd->driver->disable_transmit_sa)
+ return -1;
+ return hapd->driver->disable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct ieee802_1x_kay_ctx *kay_ctx;
+ struct ieee802_1x_kay *res = NULL;
+ enum macsec_policy policy;
+
+ ieee802_1x_dealloc_kay_sm_hapd(hapd);
+
+ if (!hapd->conf || hapd->conf->macsec_policy == 0)
+ return 0;
+
+ if (hapd->conf->macsec_policy == 1) {
+ if (hapd->conf->macsec_integ_only == 1)
+ policy = SHOULD_SECURE;
+ else
+ policy = SHOULD_ENCRYPT;
+ } else {
+ policy = DO_NOT_SECURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: if_name=%s", __func__, hapd->conf->iface);
+ kay_ctx = os_zalloc(sizeof(*kay_ctx));
+ if (!kay_ctx)
+ return -1;
+
+ kay_ctx->ctx = hapd;
+
+ kay_ctx->macsec_init = hapd_macsec_init;
+ kay_ctx->macsec_deinit = hapd_macsec_deinit;
+ kay_ctx->macsec_get_capability = hapd_macsec_get_capability;
+ kay_ctx->enable_protect_frames = hapd_enable_protect_frames;
+ kay_ctx->enable_encrypt = hapd_enable_encrypt;
+ kay_ctx->set_replay_protect = hapd_set_replay_protect;
+ kay_ctx->set_current_cipher_suite = hapd_set_current_cipher_suite;
+ kay_ctx->enable_controlled_port = hapd_enable_controlled_port;
+ kay_ctx->get_receive_lowest_pn = hapd_get_receive_lowest_pn;
+ kay_ctx->get_transmit_next_pn = hapd_get_transmit_next_pn;
+ kay_ctx->set_transmit_next_pn = hapd_set_transmit_next_pn;
+ kay_ctx->create_receive_sc = hapd_create_receive_sc;
+ kay_ctx->delete_receive_sc = hapd_delete_receive_sc;
+ kay_ctx->create_receive_sa = hapd_create_receive_sa;
+ kay_ctx->delete_receive_sa = hapd_delete_receive_sa;
+ kay_ctx->enable_receive_sa = hapd_enable_receive_sa;
+ kay_ctx->disable_receive_sa = hapd_disable_receive_sa;
+ kay_ctx->create_transmit_sc = hapd_create_transmit_sc;
+ kay_ctx->delete_transmit_sc = hapd_delete_transmit_sc;
+ kay_ctx->create_transmit_sa = hapd_create_transmit_sa;
+ kay_ctx->delete_transmit_sa = hapd_delete_transmit_sa;
+ kay_ctx->enable_transmit_sa = hapd_enable_transmit_sa;
+ kay_ctx->disable_transmit_sa = hapd_disable_transmit_sa;
+
+ res = ieee802_1x_kay_init(kay_ctx, policy,
+ hapd->conf->macsec_replay_protect,
+ hapd->conf->macsec_replay_window,
+ hapd->conf->macsec_offload,
+ hapd->conf->macsec_port,
+ hapd->conf->mka_priority,
+ hapd->conf->macsec_csindex,
+ hapd->conf->iface,
+ hapd->own_addr);
+ /* ieee802_1x_kay_init() frees kay_ctx on failure */
+ if (!res)
+ return -1;
+
+ hapd->kay = res;
+
+ return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+ if (!hapd->kay)
+ return;
+
+ ieee802_1x_kay_deinit(hapd->kay);
+ hapd->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *msk, size_t *len)
+{
+ const u8 *key;
+ size_t keylen;
+
+ if (!sta->eapol_sm)
+ return -1;
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+ if (key == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "MACsec: Failed to get MSK from EAPOL state machines");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "MACsec: Successfully fetched key (len=%lu)",
+ (unsigned long) keylen);
+ wpa_hexdump_key(MSG_DEBUG, "MSK: ", key, keylen);
+
+ if (keylen > *len)
+ keylen = *len;
+ os_memcpy(msk, key, keylen);
+ *len = keylen;
+
+ return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ const u8 *sid;
+ size_t sid_len;
+ struct mka_key_name *ckn;
+ struct mka_key *cak;
+ struct mka_key *msk;
+ void *res = NULL;
+
+ if (!hapd->kay || hapd->kay->policy == DO_NOT_SECURE)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.1X: External notification - Create MKA for "
+ MACSTR, MAC2STR(sta->addr));
+
+ msk = os_zalloc(sizeof(*msk));
+ ckn = os_zalloc(sizeof(*ckn));
+ cak = os_zalloc(sizeof(*cak));
+ if (!msk || !ckn || !cak)
+ goto fail;
+
+ msk->len = DEFAULT_KEY_LEN;
+ if (ieee802_1x_auth_get_msk(hapd, sta, msk->key, &msk->len)) {
+ wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+ goto fail;
+ }
+
+ sid = ieee802_1x_get_session_id(sta->eapol_sm, &sid_len);
+ if (!sid) {
+ wpa_printf(MSG_ERROR,
+ "IEEE 802.1X: Could not get EAP Session Id");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "own_addr", hapd->own_addr, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "sta_addr", sta->addr, ETH_ALEN);
+
+ /* Derive CAK from MSK */
+ cak->len = DEFAULT_KEY_LEN;
+ if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, hapd->own_addr,
+ sta->addr, cak->key, cak->len)) {
+ wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CAK failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len);
+
+ /* Derive CKN from MSK */
+ ckn->len = DEFAULT_CKN_LEN;
+ if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, hapd->own_addr,
+ sta->addr, sid, sid_len, ckn->name)) {
+ wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CKN failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len);
+
+ res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, EAP_EXCHANGE,
+ true);
+
+fail:
+ bin_clear_free(msk, sizeof(*msk));
+ os_free(ckn);
+ bin_clear_free(cak, sizeof(*cak));
+
+ return res;
+}
+
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct mka_key *cak;
+ struct mka_key_name *ckn;
+ void *res = NULL;
+
+ if ((hapd->conf->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+ goto end;
+
+ ckn = os_zalloc(sizeof(*ckn));
+ if (!ckn)
+ goto end;
+
+ cak = os_zalloc(sizeof(*cak));
+ if (!cak)
+ goto free_ckn;
+
+ if (ieee802_1x_alloc_kay_sm_hapd(hapd, sta) < 0 || !hapd->kay)
+ goto free_cak;
+
+ if (hapd->kay->policy == DO_NOT_SECURE)
+ goto dealloc;
+
+ cak->len = hapd->conf->mka_cak_len;
+ os_memcpy(cak->key, hapd->conf->mka_cak, cak->len);
+
+ ckn->len = hapd->conf->mka_ckn_len;;
+ os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len);
+
+ res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, true);
+ if (res)
+ goto free_cak;
+
+dealloc:
+ /* Failed to create MKA */
+ ieee802_1x_dealloc_kay_sm_hapd(hapd);
+free_cak:
+ os_free(cak);
+free_ckn:
+ os_free(ckn);
+end:
+ return res;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.h
new file mode 100644
index 0000000..0dd7e41
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.h
@@ -0,0 +1,51 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_KAY_H
+#define WPA_AUTH_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd);
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+}
+
+static inline void *
+ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPA_AUTH_KAY_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.c
new file mode 100644
index 0000000..c04e97d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.c
@@ -0,0 +1,2312 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+
+
+#ifdef CONFIG_WPS_UPNP
+#include "wps/wps_upnp.h"
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+ struct wps_context *wps);
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_WPS_UPNP */
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+ const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal);
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+static void hostapd_wps_nfc_clear(struct wps_context *wps);
+
+
+struct wps_for_each_data {
+ int (*func)(struct hostapd_data *h, void *ctx);
+ void *ctx;
+ struct hostapd_data *calling_hapd;
+};
+
+
+static int wps_for_each(struct hostapd_iface *iface, void *ctx)
+{
+ struct wps_for_each_data *data = ctx;
+ size_t j;
+
+ if (iface == NULL)
+ return 0;
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ int ret;
+
+ if (hapd != data->calling_hapd &&
+ (hapd->conf->wps_independent ||
+ data->calling_hapd->conf->wps_independent))
+ continue;
+
+ ret = data->func(hapd, data->ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_for_each(struct hostapd_data *hapd,
+ int (*func)(struct hostapd_data *h, void *ctx),
+ void *ctx)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct wps_for_each_data data;
+ data.func = func;
+ data.ctx = ctx;
+ data.calling_hapd = hapd;
+ if (iface->interfaces == NULL ||
+ iface->interfaces->for_each_interface == NULL)
+ return wps_for_each(iface, &data);
+ return iface->interfaces->for_each_interface(iface->interfaces,
+ wps_for_each, &data);
+}
+
+
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
+ size_t psk_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct hostapd_wpa_psk *p;
+ struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+ if (is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+ MAC2STR(mac_addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+ " P2P Device Addr " MACSTR,
+ MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+ }
+ wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+ if (psk_len != PMK_LEN) {
+ wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
+ (unsigned long) psk_len);
+ return -1;
+ }
+
+ /* Add the new PSK to runtime PSK list */
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+ os_memcpy(p->psk, psk, PMK_LEN);
+ p->wps = 1;
+
+ if (hapd->new_psk_cb) {
+ hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
+ psk, psk_len);
+ }
+
+ p->next = ssid->wpa_psk;
+ ssid->wpa_psk = p;
+
+ if (ssid->wpa_psk_file) {
+ FILE *f;
+ char hex[PMK_LEN * 2 + 1];
+
+ /* Add the new PSK to PSK list file */
+ f = fopen(ssid->wpa_psk_file, "a");
+ if (!f) {
+ wpa_printf(MSG_DEBUG, "Failed to add the PSK to '%s'",
+ ssid->wpa_psk_file);
+ return -1;
+ }
+
+ wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
+ fprintf(f, "wps=1 " MACSTR " %s\n", MAC2STR(mac_addr), hex);
+ fclose(f);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie)
+{
+ struct hostapd_data *hapd = ctx;
+ wpabuf_free(hapd->wps_beacon_ie);
+ hapd->wps_beacon_ie = beacon_ie;
+ wpabuf_free(hapd->wps_probe_resp_ie);
+ hapd->wps_probe_resp_ie = probe_resp_ie;
+ if (hapd->beacon_set_done)
+ ieee802_11_set_beacon(hapd);
+ return hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ struct hostapd_data *hapd = ctx;
+ char uuid[40], txt[400];
+ int len;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
+ len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
+ "%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
+ uuid, MAC2STR(dev->mac_addr), dev->device_name,
+ dev->manufacturer, dev->model_name,
+ dev->model_number, dev->serial_number,
+ wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+ sizeof(devtype)));
+ if (!os_snprintf_error(sizeof(txt), len))
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
+
+ if (hapd->conf->wps_pin_requests) {
+ FILE *f;
+ struct os_time t;
+ f = fopen(hapd->conf->wps_pin_requests, "a");
+ if (f == NULL)
+ return;
+ os_get_time(&t);
+ fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
+ "\t%s\n",
+ t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
+ dev->manufacturer, dev->model_name, dev->model_number,
+ dev->serial_number,
+ wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+ sizeof(devtype)));
+ fclose(f);
+ }
+}
+
+
+struct wps_stop_reg_data {
+ struct hostapd_data *current_hapd;
+ const u8 *uuid_e;
+ const u8 *dev_pw;
+ size_t dev_pw_len;
+};
+
+static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_stop_reg_data *data = ctx;
+ if (hapd != data->current_hapd && hapd->wps != NULL)
+ wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+ data->dev_pw, data->dev_pw_len);
+ return 0;
+}
+
+
+static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len)
+{
+ struct hostapd_data *hapd = ctx;
+ char uuid[40];
+ struct wps_stop_reg_data data;
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
+ MAC2STR(mac_addr), uuid);
+ if (hapd->wps_reg_success_cb)
+ hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+ mac_addr, uuid_e);
+ data.current_hapd = hapd;
+ data.uuid_e = uuid_e;
+ data.dev_pw = dev_pw;
+ data.dev_pw_len = dev_pw_len;
+ hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
+}
+
+
+static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
+ const u8 *uuid_e,
+ const u8 *pri_dev_type,
+ u16 config_methods,
+ u16 dev_password_id, u8 request_type,
+ const char *dev_name)
+{
+ struct hostapd_data *hapd = ctx;
+ char uuid[40];
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ if (dev_name == NULL)
+ dev_name = "";
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
+ " %s %s 0x%x %u %u [%s]",
+ MAC2STR(addr), uuid,
+ wps_dev_type_bin2str(pri_dev_type, devtype,
+ sizeof(devtype)),
+ config_methods, dev_password_id, request_type, dev_name);
+}
+
+
+static int hostapd_wps_lookup_pskfile_cb(void *ctx, const u8 *mac_addr,
+ const u8 **psk)
+{
+ const struct hostapd_data *hapd = ctx;
+ const struct hostapd_wpa_psk *wpa_psk;
+ const u8 *any_psk = NULL;
+ const u8 *dev_psk = NULL;
+
+ for (wpa_psk = hapd->conf->ssid.wpa_psk; wpa_psk;
+ wpa_psk = wpa_psk->next) {
+ if (!wpa_psk->wps)
+ continue;
+
+ if (!any_psk && is_zero_ether_addr(wpa_psk->addr))
+ any_psk = wpa_psk->psk;
+
+ if (mac_addr && !dev_psk &&
+ os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
+ dev_psk = wpa_psk->psk;
+ break;
+ }
+ }
+
+ if (dev_psk) {
+ *psk = dev_psk;
+ } else if (any_psk) {
+ *psk = any_psk;
+ } else {
+ *psk = NULL;
+ wpa_printf(MSG_DEBUG,
+ "WPS: No appropriate PSK in wpa_psk_file");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void wps_reload_config(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
+ if (iface->interfaces == NULL ||
+ iface->interfaces->reload_config(iface, 1) < 0) {
+ wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
+ "configuration");
+ }
+}
+
+
+void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+ /*
+ * Reduce race condition of the station trying to reconnect immediately
+ * after AP reconfiguration through WPS by rescheduling the reload
+ * timeout to happen after EAP completion rather than the originally
+ * scheduled 100 ms after new configuration became known.
+ */
+ if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
+ 1)
+ wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
+}
+
+
+static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
+ size_t attr_len)
+{
+ size_t blen = attr_len * 2 + 1;
+ char *buf = os_malloc(blen);
+ if (buf) {
+ wpa_snprintf_hex(buf, blen, attr, attr_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
+ os_free(buf);
+ }
+}
+
+
+static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+ const struct wps_credential *cred)
+{
+ struct hostapd_bss_config *bss = hapd->conf;
+
+ wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
+
+ bss->wps_state = 2;
+ if (cred->ssid_len <= SSID_MAX_LEN) {
+ os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
+ bss->ssid.ssid_len = cred->ssid_len;
+ bss->ssid.ssid_set = 1;
+ }
+
+#ifdef CONFIG_NO_TKIP
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK |
+ WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ bss->wpa = 2;
+ else
+ bss->wpa = 0;
+#else /* CONFIG_NO_TKIP */
+ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+ (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+ bss->wpa = 3;
+ else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+ bss->wpa = 2;
+ else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ bss->wpa = 1;
+ else
+ bss->wpa = 0;
+#endif /* CONFIG_NO_TKIP */
+
+ if (bss->wpa) {
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+ bss->wpa_pairwise = 0;
+ if (cred->encr_type & WPS_ENCR_AES) {
+ if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+ bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+ else
+ bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+#ifndef CONFIG_NO_TKIP
+ } else if (cred->encr_type & WPS_ENCR_TKIP)
+ bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+#endif /* CONFIG_NO_TKIP */
+ bss->rsn_pairwise = bss->wpa_pairwise;
+ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+ bss->wpa_pairwise,
+ bss->rsn_pairwise);
+
+ if (hapd->conf->wps_cred_add_sae &&
+ (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+ cred->key_len != 2 * PMK_LEN) {
+ bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
+ if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+ bss->ieee80211w =
+ MGMT_FRAME_PROTECTION_OPTIONAL;
+ bss->sae_require_mfp = 1;
+ }
+
+ if (cred->key_len >= 8 && cred->key_len < 64) {
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
+ if (bss->ssid.wpa_passphrase)
+ os_memcpy(bss->ssid.wpa_passphrase, cred->key,
+ cred->key_len);
+ hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+ } else if (cred->key_len == 64) {
+ hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+ bss->ssid.wpa_psk =
+ os_zalloc(sizeof(struct hostapd_wpa_psk));
+ if (bss->ssid.wpa_psk &&
+ hexstr2bin((const char *) cred->key,
+ bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
+ bss->ssid.wpa_psk->group = 1;
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = NULL;
+ }
+ }
+ bss->auth_algs = 1;
+ } else {
+ /*
+ * WPS 2.0 does not allow WEP to be configured, so no need to
+ * process that option here either.
+ */
+ bss->auth_algs = 1;
+ }
+
+ /* Schedule configuration reload after short period of time to allow
+ * EAP-WSC to be finished.
+ */
+ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+ NULL);
+
+ return 0;
+}
+
+
+static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+ const struct wps_credential *cred = ctx;
+ FILE *oconf, *nconf;
+ size_t len, i;
+ char *tmp_fname;
+ char buf[1024];
+ int multi_bss;
+ int wpa;
+ int pmf_changed = 0;
+
+ if (hapd->wps == NULL)
+ return 0;
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+ cred->cred_attr, cred->cred_attr_len);
+
+ wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+ wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+ cred->auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+ MAC2STR(cred->mac_addr));
+
+ if ((hapd->conf->wps_cred_processing == 1 ||
+ hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
+ hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
+ } else if (hapd->conf->wps_cred_processing == 1 ||
+ hapd->conf->wps_cred_processing == 2) {
+ struct wpabuf *attr;
+ attr = wpabuf_alloc(200);
+ if (attr && wps_build_credential_wrap(attr, cred) == 0)
+ hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
+ wpabuf_len(attr));
+ wpabuf_free(attr);
+ } else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
+
+ if (hapd->conf->wps_cred_processing == 1)
+ return 0;
+
+ os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+ hapd->wps->ssid_len = cred->ssid_len;
+ hapd->wps->encr_types = cred->encr_type;
+ hapd->wps->encr_types_rsn = cred->encr_type;
+ hapd->wps->encr_types_wpa = cred->encr_type;
+ hapd->wps->auth_types = cred->auth_type;
+ hapd->wps->ap_encr_type = cred->encr_type;
+ hapd->wps->ap_auth_type = cred->auth_type;
+ if (cred->key_len == 0) {
+ os_free(hapd->wps->network_key);
+ hapd->wps->network_key = NULL;
+ hapd->wps->network_key_len = 0;
+ } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
+ (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
+ wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
+ (unsigned long) cred->key_len);
+ return -1;
+ } else {
+ if (hapd->wps->network_key == NULL ||
+ hapd->wps->network_key_len < cred->key_len) {
+ hapd->wps->network_key_len = 0;
+ os_free(hapd->wps->network_key);
+ hapd->wps->network_key = os_malloc(cred->key_len);
+ if (hapd->wps->network_key == NULL)
+ return -1;
+ }
+ hapd->wps->network_key_len = cred->key_len;
+ os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
+ }
+ hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+
+ if (hapd->iface->config_fname == NULL)
+ return hapd_wps_reconfig_in_memory(hapd, cred);
+ len = os_strlen(hapd->iface->config_fname) + 5;
+ tmp_fname = os_malloc(len);
+ if (tmp_fname == NULL)
+ return -1;
+ os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
+
+ oconf = fopen(hapd->iface->config_fname, "r");
+ if (oconf == NULL) {
+ wpa_printf(MSG_WARNING, "WPS: Could not open current "
+ "configuration file");
+ os_free(tmp_fname);
+ return -1;
+ }
+
+ nconf = fopen(tmp_fname, "w");
+ if (nconf == NULL) {
+ wpa_printf(MSG_WARNING, "WPS: Could not write updated "
+ "configuration file");
+ os_free(tmp_fname);
+ fclose(oconf);
+ return -1;
+ }
+
+ fprintf(nconf, "# WPS configuration - START\n");
+
+ fprintf(nconf, "wps_state=2\n");
+
+ if (is_hex(cred->ssid, cred->ssid_len)) {
+ fprintf(nconf, "ssid2=");
+ for (i = 0; i < cred->ssid_len; i++)
+ fprintf(nconf, "%02x", cred->ssid[i]);
+ fprintf(nconf, "\n");
+ } else {
+ fprintf(nconf, "ssid=");
+ for (i = 0; i < cred->ssid_len; i++)
+ fputc(cred->ssid[i], nconf);
+ fprintf(nconf, "\n");
+ }
+
+#ifdef CONFIG_NO_TKIP
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK |
+ WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ wpa = 2;
+ else
+ wpa = 0;
+#else /* CONFIG_NO_TKIP */
+ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+ (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+ wpa = 3;
+ else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+ wpa = 2;
+ else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ wpa = 1;
+ else
+ wpa = 0;
+#endif /* CONFIG_NO_TKIP */
+
+ if (wpa) {
+ char *prefix;
+ int sae = 0;
+
+ fprintf(nconf, "wpa=%d\n", wpa);
+
+ fprintf(nconf, "wpa_key_mgmt=");
+ prefix = "";
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
+ fprintf(nconf, "WPA-EAP");
+ prefix = " ";
+ }
+ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+ fprintf(nconf, "%sWPA-PSK", prefix);
+ prefix = " ";
+ }
+ if (hapd->conf->wps_cred_add_sae &&
+ (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+ cred->key_len != 2 * PMK_LEN) {
+ fprintf(nconf, "%sSAE", prefix);
+ sae = 1;
+ }
+ fprintf(nconf, "\n");
+
+ if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ fprintf(nconf, "ieee80211w=%d\n",
+ MGMT_FRAME_PROTECTION_OPTIONAL);
+ pmf_changed = 1;
+ }
+ if (sae)
+ fprintf(nconf, "sae_require_mfp=1\n");
+
+ fprintf(nconf, "wpa_pairwise=");
+ prefix = "";
+ if (cred->encr_type & WPS_ENCR_AES) {
+ if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+ fprintf(nconf, "GCMP");
+ else
+ fprintf(nconf, "CCMP");
+
+ prefix = " ";
+ }
+#ifndef CONFIG_NO_TKIP
+ if (cred->encr_type & WPS_ENCR_TKIP) {
+ fprintf(nconf, "%sTKIP", prefix);
+ }
+#endif /* CONFIG_NO_TKIP */
+ fprintf(nconf, "\n");
+
+ if (cred->key_len >= 8 && cred->key_len < 64) {
+ fprintf(nconf, "wpa_passphrase=");
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ fprintf(nconf, "\n");
+ } else if (cred->key_len == 64) {
+ fprintf(nconf, "wpa_psk=");
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ fprintf(nconf, "\n");
+ } else {
+ wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
+ "for WPA/WPA2",
+ (unsigned long) cred->key_len);
+ }
+
+ fprintf(nconf, "auth_algs=1\n");
+ } else {
+ /*
+ * WPS 2.0 does not allow WEP to be configured, so no need to
+ * process that option here either.
+ */
+ fprintf(nconf, "auth_algs=1\n");
+ }
+
+ fprintf(nconf, "# WPS configuration - END\n");
+
+ multi_bss = 0;
+ while (fgets(buf, sizeof(buf), oconf)) {
+ if (os_strncmp(buf, "bss=", 4) == 0)
+ multi_bss = 1;
+ if (!multi_bss &&
+ (str_starts(buf, "ssid=") ||
+ str_starts(buf, "ssid2=") ||
+ str_starts(buf, "auth_algs=") ||
+#ifdef CONFIG_WEP
+ str_starts(buf, "wep_default_key=") ||
+ str_starts(buf, "wep_key") ||
+#endif /* CONFIG_WEP */
+ str_starts(buf, "wps_state=") ||
+ (pmf_changed && str_starts(buf, "ieee80211w=")) ||
+ str_starts(buf, "wpa=") ||
+ str_starts(buf, "wpa_psk=") ||
+ str_starts(buf, "wpa_pairwise=") ||
+ str_starts(buf, "rsn_pairwise=") ||
+ str_starts(buf, "wpa_key_mgmt=") ||
+ str_starts(buf, "wpa_passphrase="))) {
+ fprintf(nconf, "#WPS# %s", buf);
+ } else
+ fprintf(nconf, "%s", buf);
+ }
+
+ fclose(nconf);
+ fclose(oconf);
+
+ if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
+ wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
+ "configuration file: %s", strerror(errno));
+ os_free(tmp_fname);
+ return -1;
+ }
+
+ os_free(tmp_fname);
+
+ /* Schedule configuration reload after short period of time to allow
+ * EAP-WSC to be finished.
+ */
+ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+ NULL);
+
+ wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
+
+ return 0;
+}
+
+
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+ struct hostapd_data *hapd = ctx;
+ return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
+}
+
+
+static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+
+ if (hapd->conf->ap_setup_locked)
+ return;
+ if (hapd->ap_pin_failures_consecutive >= 10)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+ hapd->wps->ap_setup_locked = 0;
+ wps_registrar_update_ie(hapd->wps->registrar);
+}
+
+
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_event_pwd_auth_fail *data = ctx;
+
+ if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+ return 0;
+
+ /*
+ * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
+ * for some time if this happens multiple times to slow down brute
+ * force attacks.
+ */
+ hapd->ap_pin_failures++;
+ hapd->ap_pin_failures_consecutive++;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
+ "(%u consecutive)",
+ hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+ if (hapd->ap_pin_failures < 3)
+ return 0;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
+ hapd->wps->ap_setup_locked = 1;
+
+ wps_registrar_update_ie(hapd->wps->registrar);
+
+ if (!hapd->conf->ap_setup_locked &&
+ hapd->ap_pin_failures_consecutive >= 10) {
+ /*
+ * In indefinite lockdown - disable automatic AP PIN
+ * reenablement.
+ */
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ } else if (!hapd->conf->ap_setup_locked) {
+ if (hapd->ap_pin_lockout_time == 0)
+ hapd->ap_pin_lockout_time = 60;
+ else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+ (hapd->ap_pin_failures % 3) == 0)
+ hapd->ap_pin_lockout_time *= 2;
+
+ wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
+ hapd->ap_pin_lockout_time);
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
+ hostapd_wps_reenable_ap_pin, hapd,
+ NULL);
+ }
+
+ return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+ struct wps_event_pwd_auth_fail *data)
+{
+ /* Update WPS Status - Authentication Failure */
+ wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
+ hapd->wps_stats.status = WPS_STATUS_FAILURE;
+ hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
+ os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
+
+ hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+ return 0;
+
+ if (hapd->ap_pin_failures_consecutive == 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
+ "- total validation failures %u (%u consecutive)",
+ hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+ hapd->ap_pin_failures_consecutive = 0;
+
+ return 0;
+}
+
+
+static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
+{
+ hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
+static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
+{
+ /* Update WPS Status - PBC Overlap */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
+}
+
+
+static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
+{
+ /* Update WPS PBC Status:PBC Timeout */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
+}
+
+
+static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
+{
+ /* Update WPS PBC status - Active */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
+}
+
+
+static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
+{
+ /* Update WPS PBC status - Active */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+}
+
+
+static void hostapd_wps_event_success(struct hostapd_data *hapd,
+ struct wps_event_success *success)
+{
+ /* Update WPS status - Success */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+ hapd->wps_stats.status = WPS_STATUS_SUCCESS;
+ os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
+}
+
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+ struct wps_event_fail *fail)
+{
+ /* Update WPS status - Failure */
+ hapd->wps_stats.status = WPS_STATUS_FAILURE;
+ os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
+
+ hapd->wps_stats.failure_reason = fail->error_indication;
+
+ if (fail->error_indication > 0 &&
+ fail->error_indication < NUM_WPS_EI_VALUES) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error, fail->error_indication,
+ wps_ei_str(fail->error_indication));
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
+}
+
+
+static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
+ union wps_event_data *data)
+{
+ struct hostapd_data *hapd = ctx;
+ struct hostapd_ubus_request req;
+
+ os_memset(&req, 0, sizeof(req));
+ switch (event) {
+ case WPS_EV_M2D:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
+ break;
+ case WPS_EV_START:
+ req.type = HOSTAPD_UBUS_WPS_START;
+ req.addr = data->success.peer_macaddr;
+ hostapd_ubus_handle_event(hapd, &req);
+ break;
+ case WPS_EV_FAIL:
+ req.type = HOSTAPD_UBUS_WPS_FAIL;
+ req.addr = data->fail.peer_macaddr;
+ hostapd_ubus_handle_event(hapd, &req);
+ hostapd_wps_event_fail(hapd, &data->fail);
+ break;
+ case WPS_EV_SUCCESS:
+ req.type = HOSTAPD_UBUS_WPS_SUCCESS;
+ req.addr = data->success.peer_macaddr;
+ hostapd_ubus_handle_event(hapd, &req);
+ hostapd_wps_event_success(hapd, &data->success);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
+ break;
+ case WPS_EV_PWD_AUTH_FAIL:
+ hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+ break;
+ case WPS_EV_PBC_OVERLAP:
+ hostapd_wps_event_pbc_overlap(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
+ break;
+ case WPS_EV_PBC_TIMEOUT:
+ hostapd_wps_event_pbc_timeout(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
+ break;
+ case WPS_EV_PBC_ACTIVE:
+ hostapd_wps_event_pbc_active(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
+ break;
+ case WPS_EV_PBC_DISABLE:
+ hostapd_wps_event_pbc_disable(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
+ break;
+ case WPS_EV_ER_AP_ADD:
+ break;
+ case WPS_EV_ER_AP_REMOVE:
+ break;
+ case WPS_EV_ER_ENROLLEE_ADD:
+ break;
+ case WPS_EV_ER_ENROLLEE_REMOVE:
+ break;
+ case WPS_EV_ER_AP_SETTINGS:
+ break;
+ case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+ break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ hostapd_wps_ap_pin_success(hapd);
+ break;
+ }
+ if (hapd->wps_event_cb)
+ hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
+}
+
+
+static int hostapd_wps_rf_band_cb(void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+ WPS_RF_50GHZ :
+ hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+ WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
+{
+ wpabuf_free(hapd->wps_beacon_ie);
+ hapd->wps_beacon_ie = NULL;
+
+ wpabuf_free(hapd->wps_probe_resp_ie);
+ hapd->wps_probe_resp_ie = NULL;
+
+ if (deinit_only) {
+ if (hapd->drv_priv)
+ hostapd_reset_ap_wps_ie(hapd);
+ return;
+ }
+
+ hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+ const u8 **uuid = ctx;
+ size_t j;
+
+ if (iface == NULL)
+ return 0;
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ if (hapd->wps && !hapd->conf->wps_independent &&
+ !is_nil_uuid(hapd->wps->uuid)) {
+ *uuid = hapd->wps->uuid;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+ const u8 *uuid;
+ if (iface->interfaces == NULL ||
+ iface->interfaces->for_each_interface == NULL)
+ return NULL;
+ uuid = NULL;
+ iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
+ &uuid);
+ return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+ int *count= ctx;
+ (*count)++;
+ return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+ int count = 0;
+ if (iface->interfaces == NULL ||
+ iface->interfaces->for_each_interface == NULL)
+ return 0;
+ iface->interfaces->for_each_interface(iface->interfaces,
+ count_interface_cb, &count);
+ return count;
+}
+
+
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+ struct wps_context *wps)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ wps->dev.vendor_ext[i] = NULL;
+
+ if (hapd->conf->wps_vendor_ext[i] == NULL)
+ continue;
+
+ wps->dev.vendor_ext[i] =
+ wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+ if (wps->dev.vendor_ext[i] == NULL) {
+ while (--i >= 0)
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_set_application_ext(struct hostapd_data *hapd,
+ struct wps_context *wps)
+{
+ wpabuf_free(wps->dev.application_ext);
+
+ if (!hapd->conf->wps_application_ext) {
+ wps->dev.application_ext = NULL;
+ return 0;
+ }
+
+ wps->dev.application_ext = wpabuf_dup(hapd->conf->wps_application_ext);
+ return wps->dev.application_ext ? 0 : -1;
+}
+
+
+static void hostapd_free_wps(struct wps_context *wps)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ wps_device_data_free(&wps->dev);
+ bin_clear_free(wps->network_key, wps->network_key_len);
+ hostapd_wps_nfc_clear(wps);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ forced_memzero(wps->psk, sizeof(wps->psk));
+ os_free(wps);
+}
+
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf)
+{
+ struct wps_context *wps;
+ struct wps_registrar_config cfg;
+ u8 *multi_ap_netw_key = NULL;
+
+ if (conf->wps_state == 0) {
+ hostapd_wps_clear_ies(hapd, 0);
+ return 0;
+ }
+
+ wps = os_zalloc(sizeof(*wps));
+ if (wps == NULL)
+ return -1;
+
+ wps->cred_cb = hostapd_wps_cred_cb;
+ wps->event_cb = hostapd_wps_event_cb;
+ wps->rf_band_cb = hostapd_wps_rf_band_cb;
+ wps->cb_ctx = hapd;
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ wps->wps_state = hapd->conf->wps_state;
+ wps->ap_setup_locked = hapd->conf->ap_setup_locked;
+ if (is_nil_uuid(hapd->conf->uuid)) {
+ const u8 *uuid;
+ uuid = get_own_uuid(hapd->iface);
+ if (uuid && !conf->wps_independent) {
+ os_memcpy(wps->uuid, uuid, UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+ "interface", wps->uuid, UUID_LEN);
+ } else {
+ uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+ "address", wps->uuid, UUID_LEN);
+ }
+ } else {
+ os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+ wps->uuid, UUID_LEN);
+ }
+ wps->ssid_len = hapd->conf->ssid.ssid_len;
+ os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
+ wps->ap = 1;
+ os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
+ wps->dev.device_name = hapd->conf->device_name ?
+ os_strdup(hapd->conf->device_name) : NULL;
+ wps->dev.manufacturer = hapd->conf->manufacturer ?
+ os_strdup(hapd->conf->manufacturer) : NULL;
+ wps->dev.model_name = hapd->conf->model_name ?
+ os_strdup(hapd->conf->model_name) : NULL;
+ wps->dev.model_number = hapd->conf->model_number ?
+ os_strdup(hapd->conf->model_number) : NULL;
+ wps->dev.serial_number = hapd->conf->serial_number ?
+ os_strdup(hapd->conf->serial_number) : NULL;
+ wps->config_methods =
+ wps_config_methods_str2bin(hapd->conf->config_methods);
+ if ((wps->config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+ WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+ wpa_printf(MSG_INFO, "WPS: Converting display to "
+ "virtual_display for WPS 2.0 compliance");
+ wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+ }
+ if ((wps->config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+ "virtual_push_button for WPS 2.0 compliance");
+ wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ }
+ os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ if (hostapd_wps_set_vendor_ext(hapd, wps) < 0 ||
+ hostapd_wps_set_application_ext(hapd, wps) < 0)
+ goto fail;
+
+ wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
+
+ if (conf->wps_rf_bands) {
+ wps->dev.rf_bands = conf->wps_rf_bands;
+ } else {
+ wps->dev.rf_bands =
+ hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+ WPS_RF_50GHZ :
+ hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+ WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+ }
+
+ if (conf->wpa & WPA_PROTO_RSN) {
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+ wps->auth_types |= WPS_AUTH_WPA2PSK;
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ wps->auth_types |= WPS_AUTH_WPA2;
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE)
+ wps->auth_types |= WPS_AUTH_WPA2PSK;
+
+ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)) {
+ wps->encr_types |= WPS_ENCR_AES;
+ wps->encr_types_rsn |= WPS_ENCR_AES;
+ } else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+#ifdef CONFIG_NO_TKIP
+ wpa_printf(MSG_INFO, "WPS: TKIP not supported");
+ goto fail;
+#else /* CONFIG_NO_TKIP */
+ wps->encr_types |= WPS_ENCR_TKIP;
+ wps->encr_types_rsn |= WPS_ENCR_TKIP;
+#endif /* CONFIG_NO_TKIP */
+ }
+ }
+
+ if (conf->wpa & WPA_PROTO_WPA) {
+#ifdef CONFIG_NO_TKIP
+ if (!(conf->wpa & WPA_PROTO_RSN)) {
+ wpa_printf(MSG_INFO, "WPS: WPA(v1) not supported");
+ goto fail;
+ }
+ conf->wpa &= ~WPA_PROTO_WPA;
+#else /* CONFIG_NO_TKIP */
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+ wps->auth_types |= WPS_AUTH_WPAPSK;
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ wps->auth_types |= WPS_AUTH_WPA;
+
+ if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+ wps->encr_types |= WPS_ENCR_AES;
+ wps->encr_types_wpa |= WPS_ENCR_AES;
+ }
+ if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+ wps->encr_types |= WPS_ENCR_TKIP;
+ wps->encr_types_wpa |= WPS_ENCR_TKIP;
+ }
+#endif /* CONFIG_NO_TKIP */
+ }
+
+ if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
+ wps->encr_types |= WPS_ENCR_NONE;
+ wps->auth_types |= WPS_AUTH_OPEN;
+ }
+
+ if (conf->ssid.wpa_psk_file) {
+ /* Use per-device PSKs */
+ } else if (conf->ssid.wpa_passphrase) {
+ wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+ wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
+ } else if (conf->ssid.wpa_psk) {
+ wps->network_key = os_malloc(2 * PMK_LEN + 1);
+ if (wps->network_key == NULL)
+ goto fail;
+ wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
+ conf->ssid.wpa_psk->psk, PMK_LEN);
+ wps->network_key_len = 2 * PMK_LEN;
+#ifdef CONFIG_WEP
+ } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
+ wps->network_key = os_malloc(conf->ssid.wep.len[0]);
+ if (wps->network_key == NULL)
+ goto fail;
+ os_memcpy(wps->network_key, conf->ssid.wep.key[0],
+ conf->ssid.wep.len[0]);
+ wps->network_key_len = conf->ssid.wep.len[0];
+#endif /* CONFIG_WEP */
+ }
+
+ if (conf->ssid.wpa_psk) {
+ os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+ wps->psk_set = 1;
+ }
+
+ wps->ap_auth_type = wps->auth_types;
+ wps->ap_encr_type = wps->encr_types;
+ if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
+ /* Override parameters to enable security by default */
+#ifdef CONFIG_NO_TKIP
+ wps->auth_types = WPS_AUTH_WPA2PSK;
+ wps->encr_types = WPS_ENCR_AES;
+ wps->encr_types_rsn = WPS_ENCR_AES;
+ wps->encr_types_wpa = WPS_ENCR_AES;
+#else /* CONFIG_NO_TKIP */
+ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+ wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+ wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP;
+ wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
+#endif /* CONFIG_NO_TKIP */
+ }
+
+ if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+ hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
+ cfg.multi_ap_backhaul_ssid_len =
+ hapd->conf->multi_ap_backhaul_ssid.ssid_len;
+ cfg.multi_ap_backhaul_ssid =
+ hapd->conf->multi_ap_backhaul_ssid.ssid;
+
+ if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+ cfg.multi_ap_backhaul_network_key = (const u8 *)
+ conf->multi_ap_backhaul_ssid.wpa_passphrase;
+ cfg.multi_ap_backhaul_network_key_len =
+ os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ } else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+ multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
+ if (!multi_ap_netw_key)
+ goto fail;
+ wpa_snprintf_hex((char *) multi_ap_netw_key,
+ 2 * PMK_LEN + 1,
+ conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+ PMK_LEN);
+ cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
+ cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+ }
+ }
+
+ wps->ap_settings = conf->ap_settings;
+ wps->ap_settings_len = conf->ap_settings_len;
+
+ cfg.new_psk_cb = hostapd_wps_new_psk_cb;
+ cfg.set_ie_cb = hostapd_wps_set_ie_cb;
+ cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
+ cfg.reg_success_cb = hostapd_wps_reg_success_cb;
+ cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
+ cfg.lookup_pskfile_cb = hostapd_wps_lookup_pskfile_cb;
+ cfg.cb_ctx = hapd;
+ cfg.skip_cred_build = conf->skip_cred_build;
+ cfg.extra_cred = conf->extra_cred;
+ cfg.extra_cred_len = conf->extra_cred_len;
+ cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
+ conf->skip_cred_build;
+ cfg.dualband = interface_count(hapd->iface) > 1;
+ if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
+ (WPS_RF_50GHZ | WPS_RF_24GHZ))
+ cfg.dualband = 1;
+ if (cfg.dualband)
+ wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+ cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
+
+ wps->registrar = wps_registrar_init(wps, &cfg);
+ if (wps->registrar == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
+ goto fail;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ wps->friendly_name = hapd->conf->friendly_name;
+ wps->manufacturer_url = hapd->conf->manufacturer_url;
+ wps->model_description = hapd->conf->model_description;
+ wps->model_url = hapd->conf->model_url;
+ wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) &&
+ is_6ghz_op_class(hapd->iconf->op_class))
+ wps->use_passphrase = true;
+#endif /* CONFIG_P2P */
+ hapd->wps = wps;
+ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
+
+ return 0;
+
+fail:
+ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
+ hostapd_free_wps(wps);
+ return -1;
+}
+
+
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+ struct wps_context *wps = hapd->wps;
+
+ if (wps == NULL)
+ return 0;
+
+#ifdef CONFIG_WPS_UPNP
+ if (hostapd_wps_upnp_init(hapd, wps) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
+ wps_registrar_deinit(wps->registrar);
+ hostapd_free_wps(wps);
+ hapd->wps = NULL;
+ return -1;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ return 0;
+}
+
+
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+ wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
+ wps->ap_nfc_dev_pw_id = 0;
+ wpabuf_free(wps->ap_nfc_dh_pubkey);
+ wps->ap_nfc_dh_pubkey = NULL;
+ wpabuf_free(wps->ap_nfc_dh_privkey);
+ wps->ap_nfc_dh_privkey = NULL;
+ wpabuf_free(wps->ap_nfc_dev_pw);
+ wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
+}
+
+
+static int hostapd_wps_update_multi_ap(struct hostapd_data *hapd,
+ struct wps_registrar *reg)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 *multi_ap_backhaul_network_key = NULL;
+ size_t multi_ap_backhaul_network_key_len = 0;
+ int ret;
+
+ if (!(conf->multi_ap & FRONTHAUL_BSS) ||
+ !conf->multi_ap_backhaul_ssid.ssid_len)
+ return 0;
+
+ if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+ multi_ap_backhaul_network_key =
+ (u8 *) os_strdup(
+ conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ if (!multi_ap_backhaul_network_key)
+ return -1;
+ multi_ap_backhaul_network_key_len =
+ os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ } else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+ multi_ap_backhaul_network_key = os_malloc(2 * PMK_LEN + 1);
+ if (!multi_ap_backhaul_network_key)
+ return -1;
+ wpa_snprintf_hex((char *) multi_ap_backhaul_network_key,
+ 2 * PMK_LEN + 1,
+ conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+ PMK_LEN);
+ multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+ }
+
+ ret = wps_registrar_update_multi_ap(
+ reg, conf->multi_ap_backhaul_ssid.ssid,
+ conf->multi_ap_backhaul_ssid.ssid_len,
+ multi_ap_backhaul_network_key,
+ multi_ap_backhaul_network_key_len);
+ os_free(multi_ap_backhaul_network_key);
+
+ return ret;
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
+ if (hapd->wps == NULL) {
+ hostapd_wps_clear_ies(hapd, 1);
+ return;
+ }
+#ifdef CONFIG_WPS_UPNP
+ hostapd_wps_upnp_deinit(hapd);
+#endif /* CONFIG_WPS_UPNP */
+ wps_registrar_deinit(hapd->wps->registrar);
+ wps_free_pending_msgs(hapd->wps->upnp_msgs);
+ hostapd_free_wps(hapd->wps);
+ hapd->wps = NULL;
+ hostapd_wps_clear_ies(hapd, 1);
+}
+
+
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+ struct wps_context *wps = hapd->wps;
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (!wps)
+ return;
+
+#ifdef CONFIG_WPS_UPNP
+ wps->friendly_name = conf->friendly_name;
+ wps->manufacturer_url = conf->manufacturer_url;
+ wps->model_description = conf->model_description;
+ wps->model_url = conf->model_url;
+ wps->upc = conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+ os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len);
+ wps->ssid_len = conf->ssid.ssid_len;
+
+ /* Clear WPS settings, then fill them again */
+ os_free(wps->network_key);
+ wps->network_key = NULL;
+ wps->network_key_len = 0;
+ wps->psk_set = 0;
+ if (conf->ssid.wpa_psk_file) {
+ /* Use per-device PSKs */
+ } else if (conf->ssid.wpa_passphrase) {
+ wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+ if (!wps->network_key)
+ return;
+ wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
+ } else if (conf->ssid.wpa_psk) {
+ wps->network_key = os_malloc(2 * PMK_LEN + 1);
+ if (!wps->network_key)
+ return;
+ wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
+ conf->ssid.wpa_psk->psk, PMK_LEN);
+ wps->network_key_len = 2 * PMK_LEN;
+#ifdef CONFIG_WEP
+ } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
+ wps->network_key = os_malloc(conf->ssid.wep.len[0]);
+ if (!wps->network_key)
+ return;
+ os_memcpy(wps->network_key, conf->ssid.wep.key[0],
+ conf->ssid.wep.len[0]);
+ wps->network_key_len = conf->ssid.wep.len[0];
+#endif /* CONFIG_WEP */
+ }
+
+ if (conf->ssid.wpa_psk) {
+ os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+ wps->psk_set = 1;
+ }
+
+ hostapd_wps_update_multi_ap(hapd, wps->registrar);
+
+ hostapd_wps_set_vendor_ext(hapd, wps);
+ hostapd_wps_set_application_ext(hapd, wps);
+
+ if (conf->wps_state)
+ wps_registrar_update_ie(wps->registrar);
+ else
+ hostapd_deinit_wps(hapd);
+}
+
+
+struct wps_add_pin_data {
+ const u8 *addr;
+ const u8 *uuid;
+ const u8 *pin;
+ size_t pin_len;
+ int timeout;
+ int added;
+};
+
+
+static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_add_pin_data *data = ctx;
+ int ret;
+
+ if (hapd->wps == NULL)
+ return 0;
+ ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
+ data->uuid, data->pin, data->pin_len,
+ data->timeout);
+ if (ret == 0)
+ data->added++;
+ return ret;
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+ const char *uuid, const char *pin, int timeout)
+{
+ u8 u[UUID_LEN];
+ struct wps_add_pin_data data;
+
+ data.addr = addr;
+ data.uuid = u;
+ data.pin = (const u8 *) pin;
+ data.pin_len = os_strlen(pin);
+ data.timeout = timeout;
+ data.added = 0;
+
+ if (os_strcmp(uuid, "any") == 0)
+ data.uuid = NULL;
+ else {
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ data.uuid = u;
+ }
+ if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
+ return -1;
+ return data.added ? 0 : -1;
+}
+
+
+struct wps_button_pushed_ctx {
+ const u8 *p2p_dev_addr;
+ unsigned int count;
+};
+
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_button_pushed_ctx *data = ctx;
+
+ if (hapd->wps) {
+ data->count++;
+ return wps_registrar_button_pushed(hapd->wps->registrar,
+ data->p2p_dev_addr);
+ }
+
+ return 0;
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
+{
+ struct wps_button_pushed_ctx ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.p2p_dev_addr = p2p_dev_addr;
+ ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
+ if (ret == 0 && !ctx.count)
+ ret = -1;
+ return ret;
+}
+
+
+struct wps_cancel_ctx {
+ unsigned int count;
+};
+
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_cancel_ctx *data = ctx;
+
+ if (hapd->wps) {
+ data->count++;
+ wps_registrar_wps_cancel(hapd->wps->registrar);
+ ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL);
+ }
+
+ return 0;
+}
+
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+ struct wps_cancel_ctx ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
+ if (ret == 0 && !ctx.count)
+ ret = -1;
+ return ret;
+}
+
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+ const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal)
+{
+ struct hostapd_data *hapd = ctx;
+ struct wpabuf *wps_ie;
+ struct ieee802_11_elems elems;
+#ifdef CONFIG_WPS_5G_PREFER
+ int ignore_probe = 0;
+#endif /* CONFIG_WPS_5G_PREFER */
+
+ if (hapd->wps == NULL)
+ return 0;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+ MACSTR, MAC2STR(addr));
+ return 0;
+ }
+
+ if (elems.ssid && elems.ssid_len > 0 &&
+ (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+ os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+ 0))
+ return 0; /* Not for us */
+
+ wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps_ie == NULL)
+ return 0;
+ if (wps_validate_probe_req(wps_ie, addr) < 0) {
+ wpabuf_free(wps_ie);
+ return 0;
+ }
+
+ if (wpabuf_len(wps_ie) > 0) {
+ int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+ if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0)
+ p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS_5G_PREFER
+ ignore_probe = wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+ p2p_wildcard, hapd->iface->freq);
+#else
+ wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+ p2p_wildcard);
+#endif /* CONFIG_WPS_5G_PREFER */
+
+#ifdef CONFIG_WPS_UPNP
+ /* FIX: what exactly should be included in the WLANEvent?
+ * WPS attributes? Full ProbeReq frame? */
+ if (!p2p_wildcard)
+ upnp_wps_device_send_wlan_event(
+ hapd->wps_upnp, addr,
+ UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
+#endif /* CONFIG_WPS_UPNP */
+ }
+
+ wpabuf_free(wps_ie);
+
+#ifdef CONFIG_WPS_5G_PREFER
+ wpa_printf(MSG_DEBUG, "WPS: hostapd_wps_probe_req_rx: ignore_probe=%d ", ignore_probe);
+ if (ignore_probe)
+ return 1;
+#endif /* CONFIG_WPS_5G_PREFER */
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+
+static int hostapd_rx_req_put_wlan_response(
+ void *priv, enum upnp_wps_wlanevent_type ev_type,
+ const u8 *mac_addr, const struct wpabuf *msg,
+ enum wps_msg_type msg_type)
+{
+ struct hostapd_data *hapd = priv;
+ struct sta_info *sta;
+ struct upnp_pending_message *p;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
+ MACSTR, ev_type, MAC2STR(mac_addr));
+ wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
+ wpabuf_head(msg), wpabuf_len(msg));
+ if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
+ "PutWLANResponse WLANEventType %d", ev_type);
+ return -1;
+ }
+
+ /*
+ * EAP response to ongoing to WPS Registration. Send it to EAP-WSC
+ * server implementation for delivery to the peer.
+ */
+
+ sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
+ if (!sta) {
+ /*
+ * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
+ * Pick STA that is in an ongoing WPS registration without
+ * checking the MAC address.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
+ "on NewWLANEventMAC; try wildcard match");
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
+ break;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+ if (!sta || !(sta->flags & WLAN_STA_WPS)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
+ return 0;
+ }
+
+ if (!sta->eapol_sm) {
+ /*
+ * This can happen, e.g., if an ER sends an extra message after
+ * the station has disassociated (but not fully
+ * deauthenticated).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
+ return 0;
+ }
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ os_memcpy(p->addr, sta->addr, ETH_ALEN);
+ p->msg = wpabuf_dup(msg);
+ p->type = msg_type;
+ p->next = hapd->wps->upnp_msgs;
+ hapd->wps->upnp_msgs = p;
+
+ return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
+}
+
+
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+ struct wps_context *wps)
+{
+ struct upnp_wps_device_ctx *ctx;
+
+ if (!hapd->conf->upnp_iface)
+ return 0;
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return -1;
+
+ ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
+ if (hapd->conf->ap_pin)
+ ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
+
+ hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
+ hapd->conf->upnp_iface);
+ if (hapd->wps_upnp == NULL)
+ return -1;
+ wps->wps_upnp = hapd->wps_upnp;
+
+ return 0;
+}
+
+
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
+{
+ upnp_wps_device_deinit(hapd->wps_upnp, hapd);
+}
+
+#endif /* CONFIG_WPS_UPNP */
+
+
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+ char *buf, size_t buflen)
+{
+ if (hapd->wps == NULL)
+ return 0;
+ return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
+}
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+ hostapd_wps_ap_pin_disable(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+ hapd->ap_pin_failures = 0;
+ hapd->ap_pin_failures_consecutive = 0;
+ hapd->conf->ap_setup_locked = 0;
+ if (hapd->wps->ap_setup_locked) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+ hapd->wps->ap_setup_locked = 0;
+ wps_registrar_update_ie(hapd->wps->registrar);
+ }
+ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0,
+ hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
+{
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = NULL;
+#ifdef CONFIG_WPS_UPNP
+ upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+#endif /* CONFIG_WPS_UPNP */
+ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ return 0;
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+ hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
+}
+
+
+struct wps_ap_pin_data {
+ char pin_txt[9];
+ int timeout;
+};
+
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_ap_pin_data *data = ctx;
+
+ if (!hapd->wps)
+ return 0;
+
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = os_strdup(data->pin_txt);
+#ifdef CONFIG_WPS_UPNP
+ upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
+#endif /* CONFIG_WPS_UPNP */
+ hostapd_wps_ap_pin_enable(hapd, data->timeout);
+ return 0;
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+ unsigned int pin;
+ struct wps_ap_pin_data data;
+
+ if (wps_generate_pin(&pin) < 0)
+ return NULL;
+ os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
+ data.timeout = timeout;
+ hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+ return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+ return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+ int timeout)
+{
+ struct wps_ap_pin_data data;
+ int ret;
+
+ ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+ if (os_snprintf_error(sizeof(data.pin_txt), ret))
+ return -1;
+ data.timeout = timeout;
+ return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+}
+
+
+static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->wps)
+ wps_registrar_update_ie(hapd->wps->registrar);
+ return 0;
+}
+
+
+void hostapd_wps_update_ie(struct hostapd_data *hapd)
+{
+ hostapd_wps_for_each(hapd, wps_update_ie, NULL);
+}
+
+
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+ const char *auth, const char *encr, const char *key)
+{
+ struct wps_credential cred;
+ size_t len;
+
+ os_memset(&cred, 0, sizeof(cred));
+
+ len = os_strlen(ssid);
+ if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+ hexstr2bin(ssid, cred.ssid, len / 2))
+ return -1;
+ cred.ssid_len = len / 2;
+
+ if (os_strncmp(auth, "OPEN", 4) == 0)
+ cred.auth_type = WPS_AUTH_OPEN;
+#ifndef CONFIG_NO_TKIP
+ else if (os_strncmp(auth, "WPAPSK", 6) == 0)
+ cred.auth_type = WPS_AUTH_WPAPSK;
+#endif /* CONFIG_NO_TKIP */
+ else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ if (encr) {
+ if (os_strncmp(encr, "NONE", 4) == 0)
+ cred.encr_type = WPS_ENCR_NONE;
+#ifndef CONFIG_NO_TKIP
+ else if (os_strncmp(encr, "TKIP", 4) == 0)
+ cred.encr_type = WPS_ENCR_TKIP;
+#endif /* CONFIG_NO_TKIP */
+ else if (os_strncmp(encr, "CCMP", 4) == 0)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+ } else
+ cred.encr_type = WPS_ENCR_NONE;
+
+ if (key) {
+ len = os_strlen(key);
+ if ((len & 1) || len > 2 * sizeof(cred.key) ||
+ hexstr2bin(key, cred.key, len / 2))
+ return -1;
+ cred.key_len = len / 2;
+ }
+
+ if (!hapd->wps) {
+ wpa_printf(MSG_ERROR, "WPS: WPS config does not exist");
+ return -1;
+ }
+
+ return wps_registrar_config_ap(hapd->wps->registrar, &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+ const u8 *oob_dev_pw;
+ size_t oob_dev_pw_len;
+ int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_nfc_password_token_data *data = ctx;
+ int ret;
+
+ if (hapd->wps == NULL)
+ return 0;
+ ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+ data->oob_dev_pw,
+ data->oob_dev_pw_len);
+ if (ret == 0)
+ data->added++;
+ return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+ struct wps_parse_attr *attr)
+{
+ struct wps_nfc_password_token_data data;
+
+ data.oob_dev_pw = attr->oob_dev_password;
+ data.oob_dev_pw_len = attr->oob_dev_password_len;
+ data.added = 0;
+ if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+ return -1;
+ return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+ const struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+ if (wps_parse_msg(wps, &attr)) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+ return -1;
+ }
+
+ if (attr.oob_dev_password)
+ return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+ wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+ return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+ const struct wpabuf *data)
+{
+ const struct wpabuf *wps = data;
+ struct wpabuf *tmp = NULL;
+ int ret;
+
+ if (wpabuf_len(data) < 4)
+ return -1;
+
+ if (*wpabuf_head_u8(data) != 0x10) {
+ /* Assume this contains full NDEF record */
+ tmp = ndef_parse_wifi(data);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+ return -1;
+ }
+ wps = tmp;
+ }
+
+ ret = hostapd_wps_nfc_tag_process(hapd, wps);
+ wpabuf_free(tmp);
+ return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+ int ndef)
+{
+ struct wpabuf *ret;
+
+ if (hapd->wps == NULL)
+ return NULL;
+
+ ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+ hapd->iconf->channel);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
+{
+ struct wpabuf *ret;
+
+ if (hapd->wps == NULL)
+ return NULL;
+
+ if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+ struct wps_context *wps = hapd->wps;
+ if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+ &hapd->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+ hostapd_wps_nfc_clear(wps);
+ wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+ wps->ap_nfc_dh_pubkey =
+ wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+ wps->ap_nfc_dh_privkey =
+ wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+ hostapd_wps_nfc_clear(wps);
+ return NULL;
+ }
+ }
+
+ ret = wps_build_nfc_handover_sel(hapd->wps,
+ hapd->conf->wps_nfc_dh_pubkey,
+ hapd->own_addr, hapd->iface->freq);
+
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ struct wpabuf *wps;
+ int ret = -1;
+ u16 wsc_len;
+ const u8 *pos;
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ u16 dev_pw_id;
+
+ /*
+ * Enrollee/station is always initiator of the NFC connection handover,
+ * so use the request message here to find Enrollee public key hash.
+ */
+ wps = ndef_parse_wifi(req);
+ if (wps == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+ "payload from NFC connection handover");
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+ if (wpabuf_len(wps) < 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+ "Message");
+ goto out;
+ }
+ pos = wpabuf_head(wps);
+ wsc_len = WPA_GET_BE16(pos);
+ if (wsc_len > wpabuf_len(wps) - 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+ "in rt Wi-Fi Handover Request Message", wsc_len);
+ goto out;
+ }
+ pos += 2;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: WSC attributes in Wi-Fi Handover Request Message",
+ pos, wsc_len);
+ if (wsc_len < wpabuf_len(wps) - 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: Ignore extra data after WSC attributes",
+ pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+ }
+
+ wpabuf_set(&msg, pos, wsc_len);
+ ret = wps_parse_msg(&msg, &attr);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+ "Wi-Fi Handover Request Message");
+ goto out;
+ }
+
+ if (attr.oob_dev_password == NULL ||
+ attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+ wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+ "included in Wi-Fi Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ if (attr.uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+ "Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+ attr.oob_dev_password, attr.oob_dev_password_len);
+ dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+ WPS_OOB_PUBKEY_HASH_LEN);
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+ "%u in Wi-Fi Handover Request Message", dev_pw_id);
+ ret = -1;
+ goto out;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+ attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+ ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+ attr.oob_dev_password,
+ DEV_PW_NFC_CONNECTION_HANDOVER,
+ NULL, 0, 1);
+
+out:
+ wpabuf_free(wps);
+ return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+ if (hapd->conf->wps_nfc_pw_from_config) {
+ return wps_nfc_token_build(ndef,
+ hapd->conf->wps_nfc_dev_pw_id,
+ hapd->conf->wps_nfc_dh_pubkey,
+ hapd->conf->wps_nfc_dev_pw);
+ }
+
+ return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+ &hapd->conf->wps_nfc_dh_pubkey,
+ &hapd->conf->wps_nfc_dh_privkey,
+ &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+ struct wps_context *wps = hapd->wps;
+ struct wpabuf *pw;
+
+ if (wps == NULL)
+ return -1;
+
+ if (!hapd->conf->wps_nfc_dh_pubkey ||
+ !hapd->conf->wps_nfc_dh_privkey ||
+ !hapd->conf->wps_nfc_dev_pw ||
+ !hapd->conf->wps_nfc_dev_pw_id)
+ return -1;
+
+ hostapd_wps_nfc_clear(wps);
+ wpa_printf(MSG_DEBUG,
+ "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
+ hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
+ wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+ wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+ wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+ pw = hapd->conf->wps_nfc_dev_pw;
+ wps->ap_nfc_dev_pw = wpabuf_alloc(
+ wpabuf_len(pw) * 2 + 1);
+ if (wps->ap_nfc_dev_pw) {
+ wpa_snprintf_hex_uppercase(
+ (char *) wpabuf_put(wps->ap_nfc_dev_pw,
+ wpabuf_len(pw) * 2),
+ wpabuf_len(pw) * 2 + 1,
+ wpabuf_head(pw), wpabuf_len(pw));
+ }
+
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+ !wps->ap_nfc_dev_pw) {
+ hostapd_wps_nfc_clear(wps);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
+ hapd->conf->iface);
+ hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.h
new file mode 100644
index 0000000..204bd82
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.h
@@ -0,0 +1,92 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_HOSTAPD_H
+#define WPS_HOSTAPD_H
+
+#ifdef CONFIG_WPS
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf);
+int hostapd_init_wps_complete(struct hostapd_data *hapd);
+void hostapd_deinit_wps(struct hostapd_data *hapd);
+void hostapd_update_wps(struct hostapd_data *hapd);
+void hostapd_wps_eap_completed(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+ const char *uuid, const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr);
+int hostapd_wps_cancel(struct hostapd_data *hapd);
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+ char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+ int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+ const char *auth, const char *encr, const char *key);
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+ const struct wpabuf *data);
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+ int ndef);
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
+
+#else /* CONFIG_WPS */
+
+static inline int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf)
+{
+ return 0;
+}
+
+static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void hostapd_update_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
+ const u8 *addr,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
+{
+ return 0;
+}
+
+static inline int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.c
new file mode 100644
index 0000000..30c990f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.c
@@ -0,0 +1,143 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+
+
+int x_snoop_init(struct hostapd_data *hapd)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (!conf->isolate) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: ap_isolate must be enabled for x_snoop");
+ return -1;
+ }
+
+ if (conf->bridge[0] == '\0') {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Bridge must be configured for x_snoop");
+ return -1;
+ }
+
+ hapd->x_snoop_initialized = true;
+
+ if (!conf->snoop_iface[0] &&
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable hairpin_mode on the bridge port");
+ return -1;
+ }
+
+ if (!conf->snoop_iface[0] &&
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable proxyarp on the bridge port");
+ return -1;
+ }
+
+ if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+ 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+ return -1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (!conf->snoop_iface[0] &&
+ hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable multicast snooping on the bridge");
+ return -1;
+ }
+#endif /* CONFIG_IPV6 */
+
+ return 0;
+}
+
+
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ void (*handler)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ enum l2_packet_filter_type type)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct l2_packet_data *l2;
+ const char *ifname = conf->bridge;
+
+ if (conf->snoop_iface[0])
+ ifname = conf->snoop_iface;
+
+ l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
+ if (l2 == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to initialize L2 packet processing %s",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (l2_packet_set_packet_filter(l2, type)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to set L2 packet filter for type: %d",
+ type);
+ l2_packet_deinit(l2);
+ return NULL;
+ }
+
+ return l2;
+}
+
+
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *buf,
+ size_t len)
+{
+ int res;
+ u8 addr[ETH_ALEN];
+ u8 *dst_addr = buf;
+
+ if (!(dst_addr[0] & 0x01))
+ return;
+
+ wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
+ MACSTR " -> " MACSTR " (len %u)",
+ MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
+
+ /* save the multicast destination address for restoring it later */
+ os_memcpy(addr, buf, ETH_ALEN);
+
+ os_memcpy(buf, sta->addr, ETH_ALEN);
+ res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to send mcast to ucast converted packet to "
+ MACSTR, MAC2STR(sta->addr));
+ }
+
+ /* restore the multicast destination address */
+ os_memcpy(buf, addr, ETH_ALEN);
+}
+
+
+void x_snoop_deinit(struct hostapd_data *hapd)
+{
+ if (!hapd->x_snoop_initialized)
+ return;
+ hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+ hapd->x_snoop_initialized = false;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.h
new file mode 100644
index 0000000..e43a78d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.h
@@ -0,0 +1,56 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X_SNOOP_H
+#define X_SNOOP_H
+
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_PROXYARP
+
+int x_snoop_init(struct hostapd_data *hapd);
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ void (*handler)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ enum l2_packet_filter_type type);
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *buf,
+ size_t len);
+void x_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int x_snoop_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ void (*handler)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ enum l2_packet_filter_type type)
+{
+ return NULL;
+}
+
+static inline void
+x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ struct sta_info *sta, void *buf,
+ size_t len)
+{
+}
+
+static inline void x_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* X_SNOOP_H */