| 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 |
| |