@@ -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) &&
@@ -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 */
@@ -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;
}
@@ -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
@@ -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 */
@@ -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
@@ -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,
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(-)