From 4c901173a6a0886f83dc62541f0cced19da36f0e Mon Sep 17 00:00:00 2001 From: Yuvarani V Date: Thu, 28 Dec 2023 13:23:28 +0530 Subject: [PATCH] hostapd: add FT over DS support - Source AP's associated STA's ML address will be shared in FT action frame instead of the STA's link address as per D3.0 spec. - Target AP's Hostapd will maintain a separate list with the STA's ML address until it receive the assoc request from the station. - On receiving assoc frame, Target AP will replace the ML address with STA's Link address and completes the connectivity. Signed-off-by: Karthik M Signed-off-by: Yuvarani V --- src/ap/ap_drv_ops.c | 4 +- src/ap/ap_drv_ops.h | 2 +- src/ap/hostapd.c | 1 + src/ap/hostapd.h | 12 +++++ src/ap/ieee802_11.c | 119 +++++++++++++++++++++++++++++++++++++++++ src/ap/ieee802_11.h | 2 + src/ap/sta_info.c | 21 ++++++++ src/ap/sta_info.h | 3 ++ src/ap/wpa_auth.c | 6 +++ src/ap/wpa_auth.h | 4 +- src/ap/wpa_auth_ft.c | 33 ++++++++++-- src/ap/wpa_auth_glue.c | 81 +++++++++++++++++++++++++++- src/drivers/driver.h | 2 +- 13 files changed, 280 insertions(+), 10 deletions(-) --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -372,11 +372,11 @@ int hostapd_set_wds_sta(struct hostapd_d int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, - u16 auth_alg) + u16 auth_alg, bool is_ml) { if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL) return -EOPNOTSUPP; - return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg); + return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg, is_ml); } static int hostapd_get_scs_final_value(struct sta_info *sta, --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -125,7 +125,7 @@ hostapd_drv_send_action_cancel_wait(stru hapd->driver->send_action_cancel_wait(hapd->drv_priv); } int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, - u16 auth_alg); + u16 auth_alg, bool is_ml); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len); int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -3073,6 +3073,7 @@ static void hostapd_bss_setup_multi_link interfaces->mld = all_mld; interfaces->mld[interfaces->mld_count] = mld; interfaces->mld_count++; + dl_list_init(&hapd->mld->ft_ds_ml_stas); return; fail: --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -553,6 +553,17 @@ struct hostapd_sta_info { }; #ifdef CONFIG_IEEE80211BE + +/** + * struct hostapd_ft_over_ds_ml_sta_entry - ft over ds ml sta structure + */ +struct hostapd_ft_over_ds_ml_sta_entry { + struct dl_list list; + u8 mld_mac[ETH_ALEN]; + struct wpa_state_machine *wpa_sm; + struct wpa_authenticator *wpa_auth; +}; + /** * struct hostapd_mld - hostapd per-mld data structure */ @@ -564,6 +575,7 @@ struct hostapd_mld { struct hostapd_data *fbss; struct dl_list links; /* List HEAD of all affiliated links */ + struct dl_list ft_ds_ml_stas; }; #endif /* CONFIG_IEEE80211BE */ --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -60,6 +60,7 @@ #ifdef CONFIG_IEEE80211BE #include "scs.h" #endif +#include "wpa_auth_i.h" #ifdef CONFIG_FILS static struct wpabuf * @@ -3069,6 +3070,17 @@ static void handle_auth(struct hostapd_d return; } #endif /* CONFIG_SAE */ + { + struct hostapd_ft_over_ds_ml_sta_entry *entry; + + entry = ap_get_ft_ds_ml_sta(hapd, sa); + if (entry) { + wpa_printf(MSG_ERROR, + "handle_auth: Auth frame received with SA = FT-OVER-DS list MLD mac "MACSTR"\n", + MAC2STR(sa)); + return; + } + } sta = ap_get_sta(hapd, sa); if (sta) { @@ -3307,6 +3319,23 @@ static void handle_auth(struct hostapd_d } +void hostap_ft_ds_ml_sta_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_ft_over_ds_ml_sta_entry *entry = eloop_ctx; + + if (!entry) + return; + + wpa_printf(MSG_DEBUG, "%s: removing "MACSTR"\n", __func__, + MAC2STR(entry->mld_mac)); + if (entry->wpa_sm) + wpa_auth_sta_deinit(entry->wpa_sm); + + dl_list_del(&entry->list); + os_free(entry); +} + + static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd) { size_t num_bss_nontx; @@ -5284,6 +5313,93 @@ void fils_hlp_timeout(void *eloop_ctx, v #endif /* CONFIG_FILS */ +static const u8 * +hostapd_mlie_to_get_mld_addr_from_assoc(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int reassoc) +{ + struct ieee802_11_elems elems; + const u8 *pos; + int assoc_ies_len; + + if (!hapd->mld->mld_addr) + return NULL; + + pos = reassoc ? mgmt->u.reassoc_req.variable : mgmt->u.assoc_req.variable; + if (!pos) + return NULL; + + if (reassoc) { + len -= offsetof(struct ieee80211_mgmt, u.reassoc_req.variable); + assoc_ies_len = (int)len - (pos - mgmt->u.reassoc_req.variable); + } else { + len -= offsetof(struct ieee80211_mgmt, u.assoc_req.variable); + assoc_ies_len = (int)len - (pos - mgmt->u.assoc_req.variable); + } + + if (ieee802_11_parse_elems(pos, assoc_ies_len, + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "MLD: Failed parsing Authentication frame"); + return NULL; + } + + if (!elems.basic_mle || !elems.basic_mle_len) + return NULL; + + return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len); +} + + +static struct sta_info * +get_sta_from_ft_ds_list(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int reassoc) +{ + struct hostapd_ft_over_ds_ml_sta_entry *entry; + const u8 *sta_mld; + struct wpa_state_machine *wpa_sm; + struct sta_info *sta; + + if (!hapd->mld) + return NULL; + + sta_mld = hostapd_mlie_to_get_mld_addr_from_assoc(hapd, mgmt, len, reassoc); + entry = ap_get_ft_ds_ml_sta(hapd, sta_mld); + if (!entry) + return NULL; + + wpa_sm = entry->wpa_sm; + eloop_cancel_timeout(hostap_ft_ds_ml_sta_timeout, entry, NULL); + dl_list_del(&entry->list); + os_free(entry); + + if (wpa_sm) { + wpa_auth_sta_addr_change(wpa_sm, mgmt->sa); + if (!wpa_sm->group) + wpa_sm->group = hapd->wpa_auth->group; + } else { + wpa_printf(MSG_DEBUG, "%s : Temp reject the station as it is a existing entry", + __func__); + return NULL; + } + + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + if (wpa_sm) + wpa_auth_sta_deinit(wpa_sm); + return NULL; + } + + sta->auth_alg = WLAN_AUTH_FT; + sta->ft_over_ds = true; + sta->wpa_sm = wpa_sm; + //sta->ft_over_ds_saquery_status = sa_query_status; + + return sta; +} + + static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int rssi) @@ -5357,6 +5473,11 @@ static void handle_assoc(struct hostapd_ sta = ap_get_sta(hapd, mgmt->sa); #ifdef CONFIG_IEEE80211R_AP + if (!sta) { + wpa_printf(MSG_DEBUG, + "FT over DS: Check for STA entry with ML address"); + sta = get_sta_from_ft_ds_list(hapd, mgmt, len, reassoc); + } if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -170,6 +170,8 @@ static inline void sae_clear_retransmit_ } #endif /* CONFIG_SAE */ +void hostap_ft_ds_ml_sta_timeout(void *eloop_ctx, void *timeout_ctx); + u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, size_t len); --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -69,6 +69,26 @@ int ap_for_each_sta(struct hostapd_data } +struct hostapd_ft_over_ds_ml_sta_entry *ap_get_ft_ds_ml_sta(struct hostapd_data *hapd, + const u8 *sta) +{ + struct hostapd_ft_over_ds_ml_sta_entry *item; + + if (!hapd->mld) { + wpa_printf(MSG_ERROR, "NULL Pointer %s:%d\n", + __func__, __LINE__); + return NULL; + } + + dl_list_for_each(item, &hapd->mld->ft_ds_ml_stas, + struct hostapd_ft_over_ds_ml_sta_entry, list) { + if (os_memcmp(item->mld_mac, sta, ETH_ALEN) == 0) + return item; + } + return NULL; +} + + struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) { struct sta_info *s; --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -119,6 +119,7 @@ struct sta_info { u8 supported_rates[WLAN_SUPP_RATES_MAX]; int supported_rates_len; u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ + int ft_over_ds_saquery_status; #ifdef CONFIG_MESH enum mesh_plink_state plink_state; @@ -394,6 +395,8 @@ int ap_for_each_sta(struct hostapd_data int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), void *ctx); +struct hostapd_ft_over_ds_ml_sta_entry *ap_get_ft_ds_ml_sta(struct hostapd_data *hapd, + const u8 *sta_mld); struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -825,6 +825,12 @@ int wpa_reconfig(struct wpa_authenticato } +void wpa_auth_sta_addr_change(struct wpa_state_machine *wpa_sm, const u8 *addr) +{ + os_memcpy(wpa_sm->addr, addr, sizeof(wpa_sm->addr)); +} + + struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr) --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -365,7 +365,8 @@ struct wpa_auth_callbacks { const u8 *anonce, const u8 *eapol, size_t eapol_len); #ifdef CONFIG_IEEE80211R_AP - struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr, + bool is_ml); int (*add_sta_ft)(void *ctx, const u8 *sta_addr); int (*set_vlan)(void *ctx, const u8 *sta_addr, struct vlan_description *vlan); @@ -435,6 +436,7 @@ int wpa_auth_uses_ocv(struct wpa_state_m struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); +void wpa_auth_sta_addr_change(struct wpa_state_machine *wpa_sm, const u8 *addr); int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm); void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -674,11 +674,12 @@ static const u8 * wpa_ft_get_psk(struct static struct wpa_state_machine * -wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + bool is_ml) { if (wpa_auth->cb->add_sta == NULL) return NULL; - return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr); + return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr, is_ml); } @@ -4209,8 +4210,39 @@ static int wpa_ft_rrb_rx_request(struct u8 *resp_ies; size_t resp_ies_len; int res; + bool is_ml = false; + struct ieee802_11_elems elems; + const u8 *mld_mac; - sm = wpa_ft_add_sta(wpa_auth, sta_addr); + if (ieee802_11_parse_elems(body, len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "ERROR!! %s parse_elems failure for " MACSTR, + __func__, + MAC2STR(sta_addr)); + return -1; + } + + if (elems.basic_mle) { + mld_mac = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len); + if (!mld_mac) { + wpa_printf(MSG_ERROR, + "ERROR!! %s MLD Mac extraction from ML-IE failure\n", + __func__); + } else if (os_memcmp(mld_mac, sta_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_ERROR, + "ERROR!! %s MIMSATCH between sta_addr in FT-REQUEST fixed field and in the ML-IE "MACSTR" "MACSTR"\n", + __func__, MAC2STR(mld_mac), MAC2STR(sta_addr)); + return -1; + } + wpa_printf(MSG_MSGDUMP, + "FT over DS: Received STA "MACSTR" "MACSTR"\n", + MAC2STR(mld_mac), MAC2STR(sta_addr)); + is_ml = true; + } else { + is_ml = false; + } + + sm = wpa_ft_add_sta(wpa_auth, sta_addr, is_ml); if (sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " "RRB Request"); --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1130,7 +1130,80 @@ static int hostapd_wpa_auth_send_ft_acti static struct wpa_state_machine * -hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) +hostapd_wpa_auth_add_sta_ml(struct hostapd_data *hapd, const u8 *sta_mld) +{ + struct sta_info *sta_ml_obj; + bool new_allocation = false; + bool ft_ds_list_found = false; + struct hostapd_ft_over_ds_ml_sta_entry *entry; + struct wpa_state_machine *wpa_sm = NULL; + + if (!hapd->mld) { + wpa_printf(MSG_ERROR, "%s hapd is not MLO\n", __func__); + return NULL; + } + + entry = ap_get_ft_ds_ml_sta(hapd, sta_mld); + if (!entry) { + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) { + wpa_printf(MSG_ERROR, + "%s: failed to allocate ft_ds_ml_entry\n", + __func__); + return NULL; + } + new_allocation = true; + os_memcpy(entry->mld_mac, sta_mld, 6); + entry->wpa_auth = hapd->wpa_auth; + } else { + ft_ds_list_found = true; + } + + sta_ml_obj = ap_get_sta(hapd, sta_mld); + if (sta_ml_obj) + wpa_sm = sta_ml_obj->wpa_sm; + + if (!wpa_sm) { + /* + * entry->wpa_sm is marked only if wpa-sm is newly + * allocated during the FT-Over-DS entry addition here. + * Otherwise entry-wpa_sm remains as NULL + * + * On receiving assoc-request if a sta_info node has to be + * created entry->wpa_sm should be available to attach to that + * sta_info. + */ + entry->wpa_sm = + wpa_auth_sta_init(hapd->wpa_auth, sta_mld, NULL); + wpa_sm = entry->wpa_sm; + } + + if (wpa_sm == NULL) { + wpa_printf(MSG_ERROR, "%s: wpa_sm NULL\n", __func__); + if (new_allocation) + os_free(entry); + return NULL; + } + + if (ft_ds_list_found) + /* + * Cancel the existing timer + */ + eloop_cancel_timeout(hostap_ft_ds_ml_sta_timeout, sta_ml_obj, NULL); + else + dl_list_add(&hapd->mld->ft_ds_ml_stas, &entry->list); + + /* + * Expect FT-Assoc at least 5 seconds after receiving FT-Request, + * otherwise remove from list + */ + eloop_register_timeout(5, 0, hostap_ft_ds_ml_sta_timeout, sta_ml_obj, NULL); + return wpa_sm; +} + + +static struct wpa_state_machine * +hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr, bool is_ml) { struct hostapd_data *hapd = ctx; struct sta_info *sta; @@ -1139,7 +1212,7 @@ hostapd_wpa_auth_add_sta(void *ctx, cons wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR " based on WPA authenticator callback", MAC2STR(sta_addr)); - ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT); + ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT, is_ml); /* * The expected return values from hostapd_add_sta_node() are @@ -1150,6 +1223,9 @@ hostapd_wpa_auth_add_sta(void *ctx, cons if (ret < 0 && ret != -EOPNOTSUPP) return NULL; + if (is_ml) + return hostapd_wpa_auth_add_sta_ml(hapd, sta_addr); + sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -4503,7 +4503,7 @@ struct wpa_driver_ops { * This function adds the station node in the driver, when * the station gets added by FT-over-DS. */ - int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); + int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg, bool is_ml); /** * sched_scan - Request the driver to initiate scheduled scan