blob: e459fed0f877b05e3941b73ba0de96a93440f0d3 [file] [log] [blame]
developer05f3b2b2024-08-19 19:17:34 +08001From 2f6ed87f722be77d39188f84158b27945c6f41ef Mon Sep 17 00:00:00 2001
2From: Shayne Chen <shayne.chen@mediatek.com>
3Date: Mon, 29 Apr 2024 12:04:52 +0800
4Subject: [PATCH 118/126] mtk: hostapd: support MLD AP affiliated link removal
5
6Support ap link removal of MLD reconfiguration. Main changes include:
7- support to trigger it from hostapd_cli, with the following command:
8 hostapd_cli -i ap-mld-1 -l <link_id> link_remove count=10
9
10- handle NL80211_CMD_LINKS_REMOVED event
11- modify some parts that will teardown whole VIF or STA when removing
12 a link
13- handle case that the setup link sta is removed
14
15Note that currently the code only supports to remove one link at one time.
16
17Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
18---
19 hostapd/ctrl_iface.c | 26 ++++++-
20 hostapd/hostapd_cli.c | 8 +++
21 src/ap/ap_drv_ops.c | 7 ++
22 src/ap/beacon.c | 4 ++
23 src/ap/drv_callbacks.c | 3 +
24 src/ap/hostapd.c | 110 +++++++++++++++++++++++------
25 src/ap/hostapd.h | 2 +
26 src/ap/wpa_auth.c | 6 ++
27 src/ap/wpa_auth.h | 3 +
28 src/drivers/driver.h | 7 ++
29 src/drivers/driver_nl80211.c | 5 ++
30 src/drivers/driver_nl80211_event.c | 34 ++++++++-
31 12 files changed, 188 insertions(+), 27 deletions(-)
32
33diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
34index c8456513e..6db9fa617 100644
35--- a/hostapd/ctrl_iface.c
36+++ b/hostapd/ctrl_iface.c
37@@ -3903,11 +3903,31 @@ static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
38 static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
39 char *buf, size_t buflen)
40 {
41+ char *token, *context = NULL;
42+ u32 count = 0;
43 int ret;
44- u32 count = atoi(cmd);
45
46- if (!count)
47- count = 1;
48+ while ((token = str_token(cmd, " ", &context))) {
49+ if (os_strncmp(token, "count=", 6) == 0) {
50+ count = atoi(token + 6);
51+ continue;
52+ }
53+
54+ wpa_printf(MSG_ERROR, "CTRL: Invalid LINK_REMOVE parameter: %s",
55+ token);
56+ return -1;
57+ }
58+
59+ if (!count) {
60+ wpa_printf(MSG_ERROR, "Invalid ap removal count");
61+ return -1;
62+ }
63+
64+ /* limit total countdown time to be multiple of second */
65+ if ((hapd->iconf->beacon_int * count) % 1000) {
66+ wpa_printf(MSG_ERROR, "Total countdown time should be multiple of second");
67+ return -1;
68+ }
69
70 ret = hostapd_link_remove(hapd, count);
71 if (ret == 0) {
72diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
73index 12ee0a18f..9dcc0d74b 100644
74--- a/hostapd/hostapd_cli.c
75+++ b/hostapd/hostapd_cli.c
76@@ -1785,6 +1785,12 @@ static int hostapd_cli_cmd_link_add(struct wpa_ctrl *ctrl, int argc,
77 return hostapd_cli_cmd(ctrl, "LINK_ADD", 1, argc, argv);
78 }
79
80+static int hostapd_cli_cmd_link_remove(struct wpa_ctrl *ctrl, int argc,
81+ char *argv[])
82+{
83+ return hostapd_cli_cmd(ctrl, "LINK_REMOVE", 1, argc, argv);
84+}
85+
86 struct hostapd_cli_cmd {
87 const char *cmd;
88 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
89@@ -2047,6 +2053,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
90 " = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
91 { "link_add", hostapd_cli_cmd_link_add, NULL,
92 " = Add a new link to a MLD AP"},
93+ { "link_remove", hostapd_cli_cmd_link_remove, NULL,
94+ " [count=<count>] = Remove affiliated link of a MLD AP"},
95 { NULL, NULL, NULL, NULL }
96 };
97
98diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
99index f9ec9a689..8b6aed0c7 100644
100--- a/src/ap/ap_drv_ops.c
101+++ b/src/ap/ap_drv_ops.c
102@@ -332,6 +332,13 @@ int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
103 return 0;
104 }
105
106+ if (hapd->conf->mld_ap && hapd->mld->removed_links) {
107+ wpa_printf(MSG_DEBUG,
108+ "%s: Do not update station flags (" MACSTR ")",
109+ " during ap link removal", __func__, MAC2STR(sta->addr));
110+ return 0;
111+ }
112+
113 flags_or = total_flags & set_flags;
114 flags_and = total_flags | ~set_flags;
115 return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
116diff --git a/src/ap/beacon.c b/src/ap/beacon.c
117index 8a9569549..5293ee4c1 100644
118--- a/src/ap/beacon.c
119+++ b/src/ap/beacon.c
120@@ -2794,6 +2794,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
121 if (hapd->conf->mld_ap && !hapd->mld->started && hapd->beacon_set_done)
122 return 0;
123
124+ /* skip setting beacon during ap link removal */
125+ if (hapd->conf->mld_ap && hapd->mld->removed_links)
126+ return 0;
127+
128 if (!hapd->drv_priv) {
129 wpa_printf(MSG_ERROR, "Interface is disabled");
130 return -1;
131diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
132index 7ae26ab7c..9deb87c3d 100644
133--- a/src/ap/drv_callbacks.c
134+++ b/src/ap/drv_callbacks.c
135@@ -2919,6 +2919,9 @@ void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
136 hapd->conf->iface);
137 hostapd_event_color_change(hapd, true);
138 break;
139+ case EVENT_LINK_RECONFIG:
140+ hostapd_link_remove_cb(hapd, data->reconfig_info.removed_links);
141+ break;
142 #endif /* CONFIG_IEEE80211AX */
143 default:
144 wpa_printf(MSG_DEBUG, "Unknown event %d", event);
145diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
146index 7518a8b53..e34bc1fa8 100644
147--- a/src/ap/hostapd.c
148+++ b/src/ap/hostapd.c
149@@ -428,30 +428,74 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
150
151 #define TU_TO_USEC(_val) ((_val) * 1024)
152
153-static void hostapd_link_remove_timeout_handler(void *eloop_data,
154- void *user_ctx)
155+static void hostapd_switch_sta_setup_link(struct hostapd_data *hapd)
156 {
157- struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
158+ struct hostapd_data *p_hapd, *new_setup_hapd = NULL;
159+ struct sta_info *cur, *sta;
160+
161+ /* use the current least link_id as new setup link */
162+ for_each_mld_link(p_hapd, hapd) {
163+ if (p_hapd == hapd)
164+ continue;
165+ if (!new_setup_hapd || p_hapd->mld_link_id < new_setup_hapd->mld_link_id)
166+ new_setup_hapd = p_hapd;
167+ }
168
169- if (hapd->eht_mld_link_removal_count == 0)
170+ if (!new_setup_hapd)
171 return;
172- hapd->eht_mld_link_removal_count--;
173
174- wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
175- hapd->mld_link_id,
176- hapd->eht_mld_link_removal_count);
177+ sta = hapd->sta_list;
178+ while (sta) {
179+ struct sta_info *new_setup_sta;
180
181- ieee802_11_set_beacon(hapd);
182+ cur = sta;
183+ sta = sta->next;
184+ if (hostapd_sta_is_link_sta(hapd, cur))
185+ continue;
186
187- if (!hapd->eht_mld_link_removal_count) {
188- hostapd_free_link_stas(hapd);
189- hostapd_disable_iface(hapd->iface);
190- return;
191+ new_setup_sta = ap_get_sta(new_setup_hapd, cur->addr);
192+ if (!new_setup_sta)
193+ continue;
194+
195+ new_setup_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
196+ new_setup_sta->mld_assoc_sta = new_setup_sta;
197+ switch_setup_wpa_auth(new_setup_sta->wpa_sm,
198+ new_setup_hapd->wpa_auth);
199+ new_setup_sta->flags = cur->flags;
200+ cur->flags &= ~(WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
201+
202+ for_each_mld_link(p_hapd, hapd) {
203+ struct sta_info *p_sta;
204+
205+ p_sta = ap_get_sta(p_hapd, new_setup_sta->addr);
206+ if (!p_sta)
207+ continue;
208+
209+ p_sta->mld_assoc_link_id = new_setup_hapd->mld_link_id;
210+ p_sta->mld_assoc_sta = new_setup_sta;
211+ }
212+
213+ wpa_printf(MSG_INFO, "Switch assoc link of station " MACSTR " from %u to %u",
214+ MAC2STR(cur->addr), hapd->mld_link_id, new_setup_hapd->mld_link_id);
215 }
216+}
217
218- eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
219- hostapd_link_remove_timeout_handler,
220- hapd, NULL);
221+static void hostapd_link_remove_timeout_handler(void *eloop_data,
222+ void *user_ctx)
223+{
224+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
225+ struct hostapd_mld *mld = hapd->mld;
226+ u8 link_id = hapd->mld_link_id;
227+
228+ if (!mld || !mld->removed_links)
229+ return;
230+
231+ wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u", hapd->mld_link_id);
232+
233+ hostapd_switch_sta_setup_link(hapd);
234+ hostapd_free_link_stas(hapd);
235+ hostapd_disable_iface(hapd->iface);
236+ mld->removed_links &= ~BIT(link_id);
237 }
238
239
240@@ -465,16 +509,34 @@ int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
241 hapd->mld_link_id, count);
242
243 hapd->eht_mld_link_removal_count = count;
244- hapd->eht_mld_bss_param_change++;
245-
246- eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
247- hostapd_link_remove_timeout_handler,
248- hapd, NULL);
249+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_RECONFIG);
250
251 ieee802_11_set_beacon(hapd);
252+
253+ hapd->eht_mld_link_removal_count = 0;
254+ hapd->mld->removed_links |= BIT(hapd->mld_link_id);
255 return 0;
256 }
257
258+
259+void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links)
260+{
261+ struct hostapd_data *link;
262+
263+ if (!hapd->conf->mld_ap)
264+ return;
265+
266+ for_each_mld_link(link, hapd) {
267+ if (!(BIT(link->mld_link_id) & removed_links))
268+ continue;
269+
270+ link->eht_mld_bss_critical_update = 0;
271+ eloop_register_timeout(0, 0,
272+ hostapd_link_remove_timeout_handler,
273+ link, NULL);
274+ }
275+}
276+
277 #endif /* CONFIG_TESTING_OPTIONS */
278 #endif /* CONFIG_IEEE80211BE */
279
280@@ -848,7 +910,9 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
281 void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
282 {
283 hostapd_free_stas(hapd);
284- hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
285+ /* do not flush stations during ap link removal */
286+ if (!hapd->conf->mld_ap || !hapd->mld->removed_links)
287+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
288 #ifdef CONFIG_WEP
289 hostapd_clear_wep(hapd);
290 #endif /* CONFIG_WEP */
291@@ -1334,7 +1398,7 @@ static int hostapd_start_beacon(struct hostapd_data *hapd,
292 }
293
294 if (flush_old_stations && !conf->start_disabled &&
295- conf->broadcast_deauth) {
296+ conf->broadcast_deauth && (hapd->conf->mld_ap && !hapd->mld->started)) {
297 u8 addr[ETH_ALEN];
298
299 /* Should any previously associated STA not have noticed that
300diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
301index 668f1c44d..c6c286304 100644
302--- a/src/ap/hostapd.h
303+++ b/src/ap/hostapd.h
304@@ -551,6 +551,7 @@ struct hostapd_mld {
305 bool started;
306 u16 link_reconf_in_progress;
307 u16 active_links;
308+ u16 removed_links;
309
310 struct hostapd_data *fbss;
311 struct dl_list links; /* List head of all affiliated links */
312@@ -922,6 +923,7 @@ int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
313 struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
314 u8 link_id);
315 int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
316+void hostapd_link_remove_cb(struct hostapd_data *hapd, u16 removed_links);
317 bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
318 struct hostapd_data *hapd2);
319 u8 hostapd_get_mld_id(struct hostapd_data *hapd);
320diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
321index 6cb5a4be7..8fb52b697 100644
322--- a/src/ap/wpa_auth.c
323+++ b/src/ap/wpa_auth.c
324@@ -7428,3 +7428,9 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
325 }
326 #endif /* CONFIG_IEEE80211BE */
327 }
328+
329+void switch_setup_wpa_auth(struct wpa_state_machine *sm,
330+ struct wpa_authenticator *wpa_auth)
331+{
332+ sm->wpa_auth = wpa_auth;
333+}
334diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
335index b22c4199b..4cd46849a 100644
336--- a/src/ap/wpa_auth.h
337+++ b/src/ap/wpa_auth.h
338@@ -681,4 +681,7 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
339 sm->mld_links[link_id].wpa_auth && \
340 sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
341
342+void switch_setup_wpa_auth(struct wpa_state_machine *sm,
343+ struct wpa_authenticator *wpa_auth);
344+
345 #endif /* WPA_AUTH_H */
346diff --git a/src/drivers/driver.h b/src/drivers/driver.h
347index 498eff91f..eb2a48381 100644
348--- a/src/drivers/driver.h
349+++ b/src/drivers/driver.h
350@@ -7062,6 +7062,13 @@ union wpa_event_data {
351 u8 valid_links;
352 struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
353 } t2l_map_info;
354+
355+ /**
356+ * struct reconfig_info - Data for EVENT_LINK_RECONFIG
357+ */
358+ struct reconfig_info {
359+ u16 removed_links;
360+ } reconfig_info;
361 };
362
363 /**
364diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
365index 639c5e7e1..1d334b75f 100644
366--- a/src/drivers/driver_nl80211.c
367+++ b/src/drivers/driver_nl80211.c
368@@ -1117,6 +1117,11 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
369
370 if (w == NULL)
371 return;
372+
373+ /* do not clear wiphy data if there are still more than one links */
374+ if (bss->valid_links && (bss->valid_links & (bss->valid_links - 1)))
375+ return;
376+
377 bss->wiphy_data = NULL;
378 dl_list_del(&bss->wiphy_list);
379
380diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
381index efdb8ef7f..24a4bf3cf 100644
382--- a/src/drivers/driver_nl80211_event.c
383+++ b/src/drivers/driver_nl80211_event.c
384@@ -3930,6 +3930,38 @@ static void nl80211_obss_color_event(struct i802_bss *bss,
385
386 #endif /* CONFIG_IEEE80211AX */
387
388+static void nl80211_links_removed(struct wpa_driver_nl80211_data *drv,
389+ struct nlattr **tb)
390+{
391+ struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
392+ static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
393+ [NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
394+ };
395+ union wpa_event_data data = {};
396+ int rem;
397+
398+ if (!tb[NL80211_ATTR_MLO_LINKS])
399+ return;
400+
401+ nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
402+ u8 link_id;
403+
404+ if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
405+ link_attr, link_policy) != 0)
406+ continue;
407+
408+ if (!link_data[NL80211_ATTR_MLO_LINK_ID])
409+ continue;
410+
411+ link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
412+ if (link_id >= MAX_NUM_MLD_LINKS)
413+ continue;
414+ data.reconfig_info.removed_links |= BIT(link_id);
415+ }
416+
417+ wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, &data);
418+}
419+
420
421 static void do_process_drv_event(struct i802_bss *bss, int cmd,
422 struct nlattr **tb)
423@@ -4195,7 +4227,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
424 break;
425 #endif /* CONFIG_IEEE80211AX */
426 case NL80211_CMD_LINKS_REMOVED:
427- wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
428+ nl80211_links_removed(drv, tb);
429 break;
430 default:
431 wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
432--
4332.18.0
434