diff mbox series

[15/16] hostapd: MLD: add support to remove link station from AP

Message ID 20240306173947.2611965-16-quic_adisi@quicinc.com
State Accepted
Headers show
Series hostapd: scale up MLO flows | expand

Commit Message

Aditya Kumar Singh March 6, 2024, 5:39 p.m. UTC
Currently, whenever ap_free_sta() is called, it deletes the whole station
entry from kernel as well. However, with MLD station, there is a
requirement to delete only the link station.

Add support to remove link station alone from a MLD station. If the link
going to be removed is the assoc link, then whole station entry will be
removed.

Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
---
 src/ap/ap_drv_ops.c          |  4 ++--
 src/ap/ap_drv_ops.h          | 14 ++++++++++++++
 src/ap/hostapd.c             |  1 +
 src/ap/sta_info.c            | 33 ++++++++++++++++++++++++++++++++-
 src/ap/sta_info.h            |  4 ++++
 src/drivers/driver.h         |  9 +++++++++
 src/drivers/driver_nl80211.c | 29 +++++++++++++++++++++++++++++
 7 files changed, 91 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 5dfcdac3a137..587d42ee82f7 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -265,8 +265,8 @@  int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
 }
 
 
-static bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
-				    struct sta_info *sta)
+bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+			     struct sta_info *sta)
 {
 #ifdef CONFIG_IEEE80211BE
 	if (ap_sta_is_mld(hapd, sta) &&
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 9c74ef579b2f..b321f9847463 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -459,8 +459,22 @@  static inline int hostapd_drv_link_add(struct hostapd_data *hapd,
 
 }
 
+static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd,
+					      const u8 *addr)
+{
+	if (!hapd->conf->mld_ap || !hapd->driver || !hapd->drv_priv ||
+	    !hapd->driver->link_sta_remove)
+		return -1;
+
+	return hapd->driver->link_sta_remove(hapd->drv_priv,
+					     hapd->mld_link_id, addr);
+}
+
 int hostapd_if_link_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
 			   const char *ifname, u8 link_id);
 #endif /* CONFIG_IEEE80211BE */
 
+bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+			     struct sta_info *sta);
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 9dd9e6c3fd8d..863547c0de33 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -422,6 +422,7 @@  static void hostapd_link_remove_timeout_handler(void *eloop_data,
 	ieee802_11_set_beacon(hapd);
 
 	if (!hapd->eht_mld_link_removal_count) {
+		hostapd_free_link_stas(hapd);
 		hostapd_disable_iface(hapd->iface);
 		return;
 	}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 97ed805ca5c2..22d17c53bce3 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -187,6 +187,17 @@  void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta)
 
 #endif /* CONFIG_PASN */
 
+static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (hostapd_sta_is_link_sta(hapd, sta))
+		if (!hostapd_drv_link_sta_remove(hapd, sta->addr))
+			return;
+#endif /* CONFIG_IEEE80211BE */
+
+	hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int set_beacon = 0;
@@ -209,7 +220,7 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 
 	if (!hapd->iface->driver_ap_teardown &&
 	    !(sta->flags & WLAN_STA_PREAUTH)) {
-		hostapd_drv_sta_remove(hapd, sta->addr);
+		__ap_free_sta(hapd, sta);
 		sta->added_unassoc = 0;
 	}
 
@@ -453,6 +464,26 @@  void hostapd_free_stas(struct hostapd_data *hapd)
 	}
 }
 
+#ifdef CONFIG_IEEE80211BE
+void hostapd_free_link_stas(struct hostapd_data *hapd)
+{
+	struct sta_info *sta, *prev;
+
+	sta = hapd->sta_list;
+
+	while (sta) {
+		prev = sta;
+		sta = sta->next;
+
+		if (!hostapd_sta_is_link_sta(hapd, prev))
+			continue;
+
+		wpa_printf(MSG_DEBUG, "Removing link station from MLD " MACSTR,
+			   MAC2STR(prev->addr));
+		ap_free_sta(hapd, prev);
+	}
+}
+#endif /* CONFIG_IEEE80211BE */
 
 /**
  * ap_handle_timer - Per STA timer handler
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index b136ff7bfab8..546e42973a44 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -440,4 +440,8 @@  static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
 
 void ap_sta_free_sta_profile(struct mld_info *info);
 
+#ifdef CONFIG_IEEE80211BE
+void hostapd_free_link_stas(struct hostapd_data *hapd);
+#endif /* CONFIG_IEEE80211BE */
+
 #endif /* STA_INFO_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index d50cbcdb26d1..e45fac77e187 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5167,6 +5167,15 @@  struct wpa_driver_ops {
 	 * Returns: True if it is being used or else False.
 	 */
 	bool (*get_drv_shared_status)(void *priv, void *bss_ctx);
+
+	/**
+	 * link_sta_remove - Remove a link STA from a MLD STA.
+	 * @priv: Private driver interface data
+	 * @link_id: The link ID which the link STA is using
+	 * @addr: The MLD MAC address of the MLD STA
+	 * Returns: 0 on success, negative value on failure
+	 */
+	int (*link_sta_remove)(void *priv, u8 link_id, const u8 *addr);
 #endif /* CONFIG_IEEE80211BE */
 
 #ifdef CONFIG_TESTING_OPTIONS
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a0616ce39192..0f83621eca35 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -13922,6 +13922,34 @@  static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr, void *bss_ct
 	return 0;
 }
 
+#ifdef CONFIG_IEEE80211BE
+static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
+					      const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(bss->valid_links & BIT(link_id)))
+		return -ENOLINK;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK_STA)) ||
+	    nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_cmd(drv, msg);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: link_sta_remove -> REMOVE_LINK_STA on link_id %u "
+		   "from MLD STA " MACSTR ", from %s --> %d (%s)",
+		   link_id, MAC2STR(addr), bss->ifname, ret, strerror(-ret));
+
+	return ret;
+}
+#endif /* CONFIG_IEEE80211BE */
 
 #ifdef CONFIG_TESTING_OPTIONS
 
@@ -14120,6 +14148,7 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef CONFIG_IEEE80211BE
 	.if_link_remove = driver_nl80211_if_link_remove,
 	.get_drv_shared_status = wpa_driver_get_shared_status,
+	.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
 	.register_frame = testing_nl80211_register_frame,