developer | 05f3b2b | 2024-08-19 19:17:34 +0800 | [diff] [blame^] | 1 | From 2f6ed87f722be77d39188f84158b27945c6f41ef Mon Sep 17 00:00:00 2001 |
| 2 | From: Shayne Chen <shayne.chen@mediatek.com> |
| 3 | Date: Mon, 29 Apr 2024 12:04:52 +0800 |
| 4 | Subject: [PATCH 118/126] mtk: hostapd: support MLD AP affiliated link removal |
| 5 | |
| 6 | Support 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 | |
| 15 | Note that currently the code only supports to remove one link at one time. |
| 16 | |
| 17 | Signed-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 | |
| 33 | diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c |
| 34 | index 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) { |
| 72 | diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c |
| 73 | index 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 | |
| 98 | diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c |
| 99 | index 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, |
| 116 | diff --git a/src/ap/beacon.c b/src/ap/beacon.c |
| 117 | index 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; |
| 131 | diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c |
| 132 | index 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); |
| 145 | diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c |
| 146 | index 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 |
| 300 | diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h |
| 301 | index 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); |
| 320 | diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c |
| 321 | index 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 | +} |
| 334 | diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h |
| 335 | index 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 */ |
| 346 | diff --git a/src/drivers/driver.h b/src/drivers/driver.h |
| 347 | index 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 | /** |
| 364 | diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c |
| 365 | index 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 | |
| 380 | diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c |
| 381 | index 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 | -- |
| 433 | 2.18.0 |
| 434 | |