wlan-ap-Telecominfraproject/feeds/qca/hostapd/patches/r04-009-02-hostapd-add-FT-over-DS-support.patch
John Crispin 008ca9618d
Some checks failed
Build OpenWrt/uCentral images / build (cig_wf186h) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf186w) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf188n) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf189) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf196) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cybertan_eww631-a1) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cybertan_eww631-b1) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap101) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap102) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap104) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap105) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap111) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap112) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101-6e) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101e) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101e-6e) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x_2) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x_3) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x_w) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4xe) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4xi) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4xi_w) (push) Has been cancelled
Build OpenWrt/uCentral images / build (indio_um-305ax) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sercomm_ap72tip) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sonicfi_rap630c-311g) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sonicfi_rap630w-211g) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sonicfi_rap630w-311g) (push) Has been cancelled
Build OpenWrt/uCentral images / build (udaya_a6-id2) (push) Has been cancelled
Build OpenWrt/uCentral images / build (udaya_a6-od2) (push) Has been cancelled
Build OpenWrt/uCentral images / build (wallys_dr5018) (push) Has been cancelled
Build OpenWrt/uCentral images / build (wallys_dr6018) (push) Has been cancelled
Build OpenWrt/uCentral images / build (wallys_dr6018-v4) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_ax820) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_ax840) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_fap640) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_fap650) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_fap655) (push) Has been cancelled
Build OpenWrt/uCentral images / trigger-testing (push) Has been cancelled
Build OpenWrt/uCentral images / create-x64_vm-ami (push) Has been cancelled
ipq95xx: import ath12.4-cs kernel and drivers
Signed-off-by: John Crispin <john@phrozen.org>
2024-10-20 09:25:13 +02:00

522 lines
15 KiB
Diff

From 4c901173a6a0886f83dc62541f0cced19da36f0e Mon Sep 17 00:00:00 2001
From: Yuvarani V <quic_yuvarani@quicinc.com>
Date: Thu, 28 Dec 2023 13:23:28 +0530
Subject: [PATCH] hostapd: add FT over DS support
- Source AP's associated STA's ML address will be shared in FT action frame
instead of the STA's link address as per D3.0 spec.
- Target AP's Hostapd will maintain a separate list with the STA's ML address
until it receive the assoc request from the station.
- On receiving assoc frame, Target AP will replace the ML address with
STA's Link address and completes the connectivity.
Signed-off-by: Karthik M <quic_karm@quicinc.com>
Signed-off-by: Yuvarani V <quic_yuvarani@quicinc.com>
---
src/ap/ap_drv_ops.c | 4 +-
src/ap/ap_drv_ops.h | 2 +-
src/ap/hostapd.c | 1 +
src/ap/hostapd.h | 12 +++++
src/ap/ieee802_11.c | 119 +++++++++++++++++++++++++++++++++++++++++
src/ap/ieee802_11.h | 2 +
src/ap/sta_info.c | 21 ++++++++
src/ap/sta_info.h | 3 ++
src/ap/wpa_auth.c | 6 +++
src/ap/wpa_auth.h | 4 +-
src/ap/wpa_auth_ft.c | 33 ++++++++++--
src/ap/wpa_auth_glue.c | 81 +++++++++++++++++++++++++++-
src/drivers/driver.h | 2 +-
13 files changed, 280 insertions(+), 10 deletions(-)
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -372,11 +372,11 @@ int hostapd_set_wds_sta(struct hostapd_d
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
- u16 auth_alg)
+ u16 auth_alg, bool is_ml)
{
if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
return -EOPNOTSUPP;
- return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
+ return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg, is_ml);
}
static int hostapd_get_scs_final_value(struct sta_info *sta,
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -125,7 +125,7 @@ hostapd_drv_send_action_cancel_wait(stru
hapd->driver->send_action_cancel_wait(hapd->drv_priv);
}
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
- u16 auth_alg);
+ u16 auth_alg, bool is_ml);
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,
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -3073,6 +3073,7 @@ static void hostapd_bss_setup_multi_link
interfaces->mld = all_mld;
interfaces->mld[interfaces->mld_count] = mld;
interfaces->mld_count++;
+ dl_list_init(&hapd->mld->ft_ds_ml_stas);
return;
fail:
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -553,6 +553,17 @@ struct hostapd_sta_info {
};
#ifdef CONFIG_IEEE80211BE
+
+/**
+ * struct hostapd_ft_over_ds_ml_sta_entry - ft over ds ml sta structure
+ */
+struct hostapd_ft_over_ds_ml_sta_entry {
+ struct dl_list list;
+ u8 mld_mac[ETH_ALEN];
+ struct wpa_state_machine *wpa_sm;
+ struct wpa_authenticator *wpa_auth;
+};
+
/**
* struct hostapd_mld - hostapd per-mld data structure
*/
@@ -564,6 +575,7 @@ struct hostapd_mld {
struct hostapd_data *fbss;
struct dl_list links; /* List HEAD of all affiliated links */
+ struct dl_list ft_ds_ml_stas;
};
#endif /* CONFIG_IEEE80211BE */
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -60,6 +60,7 @@
#ifdef CONFIG_IEEE80211BE
#include "scs.h"
#endif
+#include "wpa_auth_i.h"
#ifdef CONFIG_FILS
static struct wpabuf *
@@ -3069,6 +3070,17 @@ static void handle_auth(struct hostapd_d
return;
}
#endif /* CONFIG_SAE */
+ {
+ struct hostapd_ft_over_ds_ml_sta_entry *entry;
+
+ entry = ap_get_ft_ds_ml_sta(hapd, sa);
+ if (entry) {
+ wpa_printf(MSG_ERROR,
+ "handle_auth: Auth frame received with SA = FT-OVER-DS list MLD mac "MACSTR"\n",
+ MAC2STR(sa));
+ return;
+ }
+ }
sta = ap_get_sta(hapd, sa);
if (sta) {
@@ -3307,6 +3319,23 @@ static void handle_auth(struct hostapd_d
}
+void hostap_ft_ds_ml_sta_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_ft_over_ds_ml_sta_entry *entry = eloop_ctx;
+
+ if (!entry)
+ return;
+
+ wpa_printf(MSG_DEBUG, "%s: removing "MACSTR"\n", __func__,
+ MAC2STR(entry->mld_mac));
+ if (entry->wpa_sm)
+ wpa_auth_sta_deinit(entry->wpa_sm);
+
+ dl_list_del(&entry->list);
+ os_free(entry);
+}
+
+
static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
{
size_t num_bss_nontx;
@@ -5284,6 +5313,93 @@ void fils_hlp_timeout(void *eloop_ctx, v
#endif /* CONFIG_FILS */
+static const u8 *
+hostapd_mlie_to_get_mld_addr_from_assoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int reassoc)
+{
+ struct ieee802_11_elems elems;
+ const u8 *pos;
+ int assoc_ies_len;
+
+ if (!hapd->mld->mld_addr)
+ return NULL;
+
+ pos = reassoc ? mgmt->u.reassoc_req.variable : mgmt->u.assoc_req.variable;
+ if (!pos)
+ return NULL;
+
+ if (reassoc) {
+ len -= offsetof(struct ieee80211_mgmt, u.reassoc_req.variable);
+ assoc_ies_len = (int)len - (pos - mgmt->u.reassoc_req.variable);
+ } else {
+ len -= offsetof(struct ieee80211_mgmt, u.assoc_req.variable);
+ assoc_ies_len = (int)len - (pos - mgmt->u.assoc_req.variable);
+ }
+
+ if (ieee802_11_parse_elems(pos, assoc_ies_len,
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Failed parsing Authentication frame");
+ return NULL;
+ }
+
+ if (!elems.basic_mle || !elems.basic_mle_len)
+ return NULL;
+
+ return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+}
+
+
+static struct sta_info *
+get_sta_from_ft_ds_list(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int reassoc)
+{
+ struct hostapd_ft_over_ds_ml_sta_entry *entry;
+ const u8 *sta_mld;
+ struct wpa_state_machine *wpa_sm;
+ struct sta_info *sta;
+
+ if (!hapd->mld)
+ return NULL;
+
+ sta_mld = hostapd_mlie_to_get_mld_addr_from_assoc(hapd, mgmt, len, reassoc);
+ entry = ap_get_ft_ds_ml_sta(hapd, sta_mld);
+ if (!entry)
+ return NULL;
+
+ wpa_sm = entry->wpa_sm;
+ eloop_cancel_timeout(hostap_ft_ds_ml_sta_timeout, entry, NULL);
+ dl_list_del(&entry->list);
+ os_free(entry);
+
+ if (wpa_sm) {
+ wpa_auth_sta_addr_change(wpa_sm, mgmt->sa);
+ if (!wpa_sm->group)
+ wpa_sm->group = hapd->wpa_auth->group;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s : Temp reject the station as it is a existing entry",
+ __func__);
+ return NULL;
+ }
+
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ if (wpa_sm)
+ wpa_auth_sta_deinit(wpa_sm);
+ return NULL;
+ }
+
+ sta->auth_alg = WLAN_AUTH_FT;
+ sta->ft_over_ds = true;
+ sta->wpa_sm = wpa_sm;
+ //sta->ft_over_ds_saquery_status = sa_query_status;
+
+ return sta;
+}
+
+
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc, int rssi)
@@ -5357,6 +5473,11 @@ static void handle_assoc(struct hostapd_
sta = ap_get_sta(hapd, mgmt->sa);
#ifdef CONFIG_IEEE80211R_AP
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "FT over DS: Check for STA entry with ML address");
+ sta = get_sta_from_ft_ds_list(hapd, mgmt, len, reassoc);
+ }
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -170,6 +170,8 @@ static inline void sae_clear_retransmit_
}
#endif /* CONFIG_SAE */
+void hostap_ft_ds_ml_sta_timeout(void *eloop_ctx, void *timeout_ctx);
+
u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd,
u8 *eid, size_t len);
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -69,6 +69,26 @@ int ap_for_each_sta(struct hostapd_data
}
+struct hostapd_ft_over_ds_ml_sta_entry *ap_get_ft_ds_ml_sta(struct hostapd_data *hapd,
+ const u8 *sta)
+{
+ struct hostapd_ft_over_ds_ml_sta_entry *item;
+
+ if (!hapd->mld) {
+ wpa_printf(MSG_ERROR, "NULL Pointer %s:%d\n",
+ __func__, __LINE__);
+ return NULL;
+ }
+
+ dl_list_for_each(item, &hapd->mld->ft_ds_ml_stas,
+ struct hostapd_ft_over_ds_ml_sta_entry, list) {
+ if (os_memcmp(item->mld_mac, sta, ETH_ALEN) == 0)
+ return item;
+ }
+ return NULL;
+}
+
+
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
{
struct sta_info *s;
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -119,6 +119,7 @@ struct sta_info {
u8 supported_rates[WLAN_SUPP_RATES_MAX];
int supported_rates_len;
u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+ int ft_over_ds_saquery_status;
#ifdef CONFIG_MESH
enum mesh_plink_state plink_state;
@@ -394,6 +395,8 @@ int ap_for_each_sta(struct hostapd_data
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
void *ctx),
void *ctx);
+struct hostapd_ft_over_ds_ml_sta_entry *ap_get_ft_ds_ml_sta(struct hostapd_data *hapd,
+ const u8 *sta_mld);
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);
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -825,6 +825,12 @@ int wpa_reconfig(struct wpa_authenticato
}
+void wpa_auth_sta_addr_change(struct wpa_state_machine *wpa_sm, const u8 *addr)
+{
+ os_memcpy(wpa_sm->addr, addr, sizeof(wpa_sm->addr));
+}
+
+
struct wpa_state_machine *
wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *p2p_dev_addr)
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -365,7 +365,8 @@ struct wpa_auth_callbacks {
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);
+ struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr,
+ bool is_ml);
int (*add_sta_ft)(void *ctx, const u8 *sta_addr);
int (*set_vlan)(void *ctx, const u8 *sta_addr,
struct vlan_description *vlan);
@@ -435,6 +436,7 @@ int wpa_auth_uses_ocv(struct wpa_state_m
struct wpa_state_machine *
wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *p2p_dev_addr);
+void wpa_auth_sta_addr_change(struct wpa_state_machine *wpa_sm, const u8 *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);
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -674,11 +674,12 @@ static const u8 * wpa_ft_get_psk(struct
static struct wpa_state_machine *
-wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ bool is_ml)
{
if (wpa_auth->cb->add_sta == NULL)
return NULL;
- return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
+ return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr, is_ml);
}
@@ -4209,8 +4210,39 @@ static int wpa_ft_rrb_rx_request(struct
u8 *resp_ies;
size_t resp_ies_len;
int res;
+ bool is_ml = false;
+ struct ieee802_11_elems elems;
+ const u8 *mld_mac;
- sm = wpa_ft_add_sta(wpa_auth, sta_addr);
+ if (ieee802_11_parse_elems(body, len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "ERROR!! %s parse_elems failure for " MACSTR,
+ __func__,
+ MAC2STR(sta_addr));
+ return -1;
+ }
+
+ if (elems.basic_mle) {
+ mld_mac = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+ if (!mld_mac) {
+ wpa_printf(MSG_ERROR,
+ "ERROR!! %s MLD Mac extraction from ML-IE failure\n",
+ __func__);
+ } else if (os_memcmp(mld_mac, sta_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_ERROR,
+ "ERROR!! %s MIMSATCH between sta_addr in FT-REQUEST fixed field and in the ML-IE "MACSTR" "MACSTR"\n",
+ __func__, MAC2STR(mld_mac), MAC2STR(sta_addr));
+ return -1;
+ }
+ wpa_printf(MSG_MSGDUMP,
+ "FT over DS: Received STA "MACSTR" "MACSTR"\n",
+ MAC2STR(mld_mac), MAC2STR(sta_addr));
+ is_ml = true;
+ } else {
+ is_ml = false;
+ }
+
+ sm = wpa_ft_add_sta(wpa_auth, sta_addr, is_ml);
if (sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
"RRB Request");
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1130,7 +1130,80 @@ static int hostapd_wpa_auth_send_ft_acti
static struct wpa_state_machine *
-hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
+hostapd_wpa_auth_add_sta_ml(struct hostapd_data *hapd, const u8 *sta_mld)
+{
+ struct sta_info *sta_ml_obj;
+ bool new_allocation = false;
+ bool ft_ds_list_found = false;
+ struct hostapd_ft_over_ds_ml_sta_entry *entry;
+ struct wpa_state_machine *wpa_sm = NULL;
+
+ if (!hapd->mld) {
+ wpa_printf(MSG_ERROR, "%s hapd is not MLO\n", __func__);
+ return NULL;
+ }
+
+ entry = ap_get_ft_ds_ml_sta(hapd, sta_mld);
+ if (!entry) {
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL) {
+ wpa_printf(MSG_ERROR,
+ "%s: failed to allocate ft_ds_ml_entry\n",
+ __func__);
+ return NULL;
+ }
+ new_allocation = true;
+ os_memcpy(entry->mld_mac, sta_mld, 6);
+ entry->wpa_auth = hapd->wpa_auth;
+ } else {
+ ft_ds_list_found = true;
+ }
+
+ sta_ml_obj = ap_get_sta(hapd, sta_mld);
+ if (sta_ml_obj)
+ wpa_sm = sta_ml_obj->wpa_sm;
+
+ if (!wpa_sm) {
+ /*
+ * entry->wpa_sm is marked only if wpa-sm is newly
+ * allocated during the FT-Over-DS entry addition here.
+ * Otherwise entry-wpa_sm remains as NULL
+ *
+ * On receiving assoc-request if a sta_info node has to be
+ * created entry->wpa_sm should be available to attach to that
+ * sta_info.
+ */
+ entry->wpa_sm =
+ wpa_auth_sta_init(hapd->wpa_auth, sta_mld, NULL);
+ wpa_sm = entry->wpa_sm;
+ }
+
+ if (wpa_sm == NULL) {
+ wpa_printf(MSG_ERROR, "%s: wpa_sm NULL\n", __func__);
+ if (new_allocation)
+ os_free(entry);
+ return NULL;
+ }
+
+ if (ft_ds_list_found)
+ /*
+ * Cancel the existing timer
+ */
+ eloop_cancel_timeout(hostap_ft_ds_ml_sta_timeout, sta_ml_obj, NULL);
+ else
+ dl_list_add(&hapd->mld->ft_ds_ml_stas, &entry->list);
+
+ /*
+ * Expect FT-Assoc at least 5 seconds after receiving FT-Request,
+ * otherwise remove from list
+ */
+ eloop_register_timeout(5, 0, hostap_ft_ds_ml_sta_timeout, sta_ml_obj, NULL);
+ return wpa_sm;
+}
+
+
+static struct wpa_state_machine *
+hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr, bool is_ml)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
@@ -1139,7 +1212,7 @@ hostapd_wpa_auth_add_sta(void *ctx, cons
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);
+ ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT, is_ml);
/*
* The expected return values from hostapd_add_sta_node() are
@@ -1150,6 +1223,9 @@ hostapd_wpa_auth_add_sta(void *ctx, cons
if (ret < 0 && ret != -EOPNOTSUPP)
return NULL;
+ if (is_ml)
+ return hostapd_wpa_auth_add_sta_ml(hapd, sta_addr);
+
sta = ap_sta_add(hapd, sta_addr);
if (sta == NULL)
return NULL;
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -4503,7 +4503,7 @@ struct wpa_driver_ops {
* This function adds the station node in the driver, when
* the station gets added by FT-over-DS.
*/
- int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg, bool is_ml);
/**
* sched_scan - Request the driver to initiate scheduled scan