blob: e459fed0f877b05e3941b73ba0de96a93440f0d3 [file] [log] [blame]
From 2f6ed87f722be77d39188f84158b27945c6f41ef Mon Sep 17 00:00:00 2001
From: Shayne Chen <shayne.chen@mediatek.com>
Date: Mon, 29 Apr 2024 12:04:52 +0800
Subject: [PATCH 118/126] mtk: hostapd: support MLD AP affiliated link removal
Support ap link removal of MLD reconfiguration. Main changes include:
- support to trigger it from hostapd_cli, with the following command:
hostapd_cli -i ap-mld-1 -l <link_id> link_remove count=10
- handle NL80211_CMD_LINKS_REMOVED event
- modify some parts that will teardown whole VIF or STA when removing
a link
- handle case that the setup link sta is removed
Note that currently the code only supports to remove one link at one time.
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
---
hostapd/ctrl_iface.c | 26 ++++++-
hostapd/hostapd_cli.c | 8 +++
src/ap/ap_drv_ops.c | 7 ++
src/ap/beacon.c | 4 ++
src/ap/drv_callbacks.c | 3 +
src/ap/hostapd.c | 110 +++++++++++++++++++++++------
src/ap/hostapd.h | 2 +
src/ap/wpa_auth.c | 6 ++
src/ap/wpa_auth.h | 3 +
src/drivers/driver.h | 7 ++
src/drivers/driver_nl80211.c | 5 ++
src/drivers/driver_nl80211_event.c | 34 ++++++++-
12 files changed, 188 insertions(+), 27 deletions(-)
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index c8456513e..6db9fa617 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -3903,11 +3903,31 @@ static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
char *buf, size_t buflen)
{
+ char *token, *context = NULL;
+ u32 count = 0;
int ret;
- u32 count = atoi(cmd);
- if (!count)
- count = 1;
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strncmp(token, "count=", 6) == 0) {
+ count = atoi(token + 6);
+ continue;
+ }
+
+ wpa_printf(MSG_ERROR, "CTRL: Invalid LINK_REMOVE parameter: %s",
+ token);
+ return -1;
+ }
+
+ if (!count) {
+ wpa_printf(MSG_ERROR, "Invalid ap removal count");
+ return -1;
+ }
+
+ /* limit total countdown time to be multiple of second */
+ if ((hapd->iconf->beacon_int * count) % 1000) {
+ wpa_printf(MSG_ERROR, "Total countdown time should be multiple of second");
+ return -1;
+ }
ret = hostapd_link_remove(hapd, count);
if (ret == 0) {
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 12ee0a18f..9dcc0d74b 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1785,6 +1785,12 @@ static int hostapd_cli_cmd_link_add(struct wpa_ctrl *ctrl, int argc,
return hostapd_cli_cmd(ctrl, "LINK_ADD", 1, argc, argv);
}
+static int hostapd_cli_cmd_link_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "LINK_REMOVE", 1, argc, argv);
+}
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -2047,6 +2053,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
" = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
{ "link_add", hostapd_cli_cmd_link_add, NULL,
" = Add a new link to a MLD AP"},
+ { "link_remove", hostapd_cli_cmd_link_remove, NULL,
+ " [count=<count>] = Remove affiliated link of a MLD AP"},
{ NULL, NULL, NULL, NULL }
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f9ec9a689..8b6aed0c7 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -332,6 +332,13 @@ int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
return 0;
}
+ if (hapd->conf->mld_ap && hapd->mld->removed_links) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update station flags (" MACSTR ")",
+ " during ap link removal", __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
flags_or = total_flags & set_flags;
flags_and = total_flags | ~set_flags;
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 8a9569549..5293ee4c1 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2794,6 +2794,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
return 0;
+ /* skip setting beacon during ap link removal */
+ if (hapd->conf->mld_ap && hapd->mld->removed_links)
+ return 0;
+
if (!hapd->drv_priv) {
wpa_printf(MSG_ERROR, "Interface is disabled");
return -1;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 7ae26ab7c..9deb87c3d 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -2919,6 +2919,9 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
hapd->conf->iface);
hostapd_event_color_change(hapd, true);
break;
+ case EVENT_LINK_RECONFIG:
+ hostapd_link_remove_cb(hapd, data->reconfig_info.removed_links);
+ break;
#endif /* CONFIG_IEEE80211AX */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 7518a8b53..e34bc1fa8 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -428,30 +428,74 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
#define TU_TO_USEC(_val) ((_val) * 1024)
-static void hostapd_link_remove_timeout_handler(void *eloop_data,
- void *user_ctx)
+static void hostapd_switch_sta_setup_link(struct hostapd_data *hapd)
{
- struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+ struct hostapd_data *p_hapd, *new_setup_hapd = NULL;
+ struct sta_info *cur, *sta;
+
+ /* use the current least link_id as new setup link */
+ for_each_mld_link(p_hapd, hapd) {
+ if (p_hapd == hapd)
+ continue;
+ if (!new_setup_hapd || p_hapd->mld_link_id < new_setup_hapd->mld_link_id)
+ new_setup_hapd = p_hapd;
+ }
- if (hapd->eht_mld_link_removal_count == 0)
+ if (!new_setup_hapd)
return;
- hapd->eht_mld_link_removal_count--;
- wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
- hapd->mld_link_id,
- hapd->eht_mld_link_removal_count);
+ sta = hapd->sta_list;
+ while (sta) {
+ struct sta_info *new_setup_sta;
- ieee802_11_set_beacon(hapd);
+ cur = sta;
+ sta = sta->next;
+ if (hostapd_sta_is_link_sta(hapd, cur))
+ continue;
- if (!hapd->eht_mld_link_removal_count) {
- hostapd_free_link_stas(hapd);
- hostapd_disable_iface(hapd->iface);
- return;
+ new_setup_sta = ap_get_sta(new_setup_hapd, cur->addr);
+ if (!new_setup_sta)
+ continue;
+
+ new_setup_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
+ new_setup_sta->mld_assoc_sta = new_setup_sta;
+ switch_setup_wpa_auth(new_setup_sta->wpa_sm,
+ new_setup_hapd->wpa_auth);
+ new_setup_sta->flags = cur->flags;
+ cur->flags &= ~(WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
+
+ for_each_mld_link(p_hapd, hapd) {
+ struct sta_info *p_sta;
+
+ p_sta = ap_get_sta(p_hapd, new_setup_sta->addr);
+ if (!p_sta)
+ continue;
+
+ p_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
+ p_sta->mld_assoc_sta = new_setup_sta;
+ }
+
+ wpa_printf(MSG_INFO, "Switch assoc link of station " MACSTR " from %u to %u",
+ MAC2STR(cur->addr), hapd->mld_link_id, new_setup_hapd->mld_link_id);
}
+}
- eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
- hostapd_link_remove_timeout_handler,
- hapd, NULL);
+static void hostapd_link_remove_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+ struct hostapd_mld *mld = hapd->mld;
+ u8 link_id = hapd->mld_link_id;
+
+ if (!mld || !mld->removed_links)
+ return;
+
+ wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u", hapd->mld_link_id);
+
+ hostapd_switch_sta_setup_link(hapd);
+ hostapd_free_link_stas(hapd);
+ hostapd_disable_iface(hapd->iface);
+ mld->removed_links &= ~BIT(link_id);
}
@@ -465,16 +509,34 @@ int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
hapd->mld_link_id, count);
hapd->eht_mld_link_removal_count = count;
- hapd->eht_mld_bss_param_change++;
-
- eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
- hostapd_link_remove_timeout_handler,
- hapd, NULL);
+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_RECONFIG);
ieee802_11_set_beacon(hapd);
+
+ hapd->eht_mld_link_removal_count = 0;
+ hapd->mld->removed_links |= BIT(hapd->mld_link_id);
return 0;
}
+
+void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links)
+{
+ struct hostapd_data *link;
+
+ if (!hapd->conf->mld_ap)
+ return;
+
+ for_each_mld_link(link, hapd) {
+ if (!(BIT(link->mld_link_id) & removed_links))
+ continue;
+
+ link->eht_mld_bss_critical_update = 0;
+ eloop_register_timeout(0, 0,
+ hostapd_link_remove_timeout_handler,
+ link, NULL);
+ }
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
@@ -848,7 +910,9 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
{
hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ /* do not flush stations during ap link removal */
+ if (!hapd->conf->mld_ap || !hapd->mld->removed_links)
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
#ifdef CONFIG_WEP
hostapd_clear_wep(hapd);
#endif /* CONFIG_WEP */
@@ -1334,7 +1398,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
}
if (flush_old_stations && !conf->start_disabled &&
- conf->broadcast_deauth) {
+ conf->broadcast_deauth && (hapd->conf->mld_ap && !hapd->mld->started)) {
u8 addr[ETH_ALEN];
/* Should any previously associated STA not have noticed that
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 668f1c44d..c6c286304 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -551,6 +551,7 @@ struct hostapd_mld {
bool started;
u16 link_reconf_in_progress;
u16 active_links;
+ u16 removed_links;
struct hostapd_data *fbss;
struct dl_list links; /* List head of all affiliated links */
@@ -922,6 +923,7 @@ 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);
int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links);
bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
struct hostapd_data *hapd2);
u8 hostapd_get_mld_id(struct hostapd_data *hapd);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 6cb5a4be7..8fb52b697 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -7428,3 +7428,9 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
}
#endif /* CONFIG_IEEE80211BE */
}
+
+void switch_setup_wpa_auth(struct wpa_state_machine *sm,
+ struct wpa_authenticator *wpa_auth)
+{
+ sm->wpa_auth = wpa_auth;
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index b22c4199b..4cd46849a 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -681,4 +681,7 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
sm->mld_links[link_id].wpa_auth && \
sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+void switch_setup_wpa_auth(struct wpa_state_machine *sm,
+ struct wpa_authenticator *wpa_auth);
+
#endif /* WPA_AUTH_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 498eff91f..eb2a48381 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -7062,6 +7062,13 @@ union wpa_event_data {
u8 valid_links;
struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
} t2l_map_info;
+
+ /**
+ * struct reconfig_info - Data for EVENT_LINK_RECONFIG
+ */
+ struct reconfig_info {
+ u16 removed_links;
+ } reconfig_info;
};
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 639c5e7e1..1d334b75f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1117,6 +1117,11 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
if (w == NULL)
return;
+
+ /* do not clear wiphy data if there are still more than one links */
+ if (bss->valid_links && (bss->valid_links & (bss->valid_links - 1)))
+ return;
+
bss->wiphy_data = NULL;
dl_list_del(&bss->wiphy_list);
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index efdb8ef7f..24a4bf3cf 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -3930,6 +3930,38 @@ static void nl80211_obss_color_event(struct i802_bss *bss,
#endif /* CONFIG_IEEE80211AX */
+static void nl80211_links_removed(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
+ static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
+ [NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
+ };
+ union wpa_event_data data = {};
+ int rem;
+
+ if (!tb[NL80211_ATTR_MLO_LINKS])
+ return;
+
+ nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
+ u8 link_id;
+
+ if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
+ link_attr, link_policy) != 0)
+ continue;
+
+ if (!link_data[NL80211_ATTR_MLO_LINK_ID])
+ continue;
+
+ link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+ data.reconfig_info.removed_links |= BIT(link_id);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, &data);
+}
+
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
@@ -4195,7 +4227,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
break;
#endif /* CONFIG_IEEE80211AX */
case NL80211_CMD_LINKS_REMOVED:
- wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
+ nl80211_links_removed(drv, tb);
break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
--
2.18.0