From: Shivani Baranwal <quic_shivbara@xxxxxxxxxxx> This commit does below - Allow driver's SME to enable MLO(Multi-Link Operation) connection when non-WPA open mode selected for the connection. - Read mult-link connection info from NL80211_CMD_CONNECT event and copy to corresponding wpa_s instance. - Hold the all the associated link BSSes scan results till the connection is active. - Authorize supplicant port for AP MLD address after successful connection. Signed-off-by: Shivani Baranwal <quic_shivbara@xxxxxxxxxxx> Co-developed-by: Veerendranath Jakkam <quic_vjakkam@xxxxxxxxxxx> Signed-off-by: Veerendranath Jakkam <quic_vjakkam@xxxxxxxxxxx> --- src/common/defs.h | 2 + src/drivers/driver.h | 22 ++++++++++ src/drivers/driver_nl80211.c | 80 +++++++++++++++++++++++++++++++--- src/drivers/driver_nl80211.h | 2 + src/drivers/driver_nl80211_event.c | 78 +++++++++++++++++++++++++++++++-- wpa_supplicant/bss.c | 22 ++++++++-- wpa_supplicant/driver_i.h | 10 +++++ wpa_supplicant/events.c | 89 ++++++++++++++++++++++++++++++++++++++ wpa_supplicant/wpa_supplicant_i.h | 7 +++ 9 files changed, 300 insertions(+), 12 deletions(-) diff --git a/src/common/defs.h b/src/common/defs.h index 4e63053..91ac4f8 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -498,4 +498,6 @@ enum frame_encryption { FRAME_ENCRYPTED = 1 }; +#define MAX_NUM_MLD_LINKS 15 + #endif /* DEFS_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 52ee8df..0685eb0 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2621,6 +2621,15 @@ struct weighted_pcl { u32 flag; /* bitmap for WEIGHTED_PCL_* */ }; +struct driver_sta_mlo_info { + u16 valid_links; + u8 ap_mld_addr[ETH_ALEN]; + struct { + u8 addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + } links[MAX_NUM_MLD_LINKS]; +}; + /** * struct wpa_driver_ops - Driver interface API definition * @@ -4669,6 +4678,19 @@ struct wpa_driver_ops { const u8 *match, size_t match_len, bool multicast); #endif /* CONFIG_TESTING_OPTIONS */ + + + /** + * get_sta_mlo_info - Get the current multi-link associtaion info + * @priv: private driver interface data + * @mlo: pointer to fill multi-link associtaion info + * Returns: 0 on success, -1 on failure + * + * This callback is used to fetch multi-link of the current association. + */ + int (*get_sta_mlo_info)(void *priv, + struct driver_sta_mlo_info *mlo_info); + }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 78c0658..5fd058b 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -270,6 +270,7 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) drv->associated = 0; os_memset(drv->bssid, 0, ETH_ALEN); drv->first_bss->freq = 0; + drv->sta_mlo_info.valid_links = 0; } @@ -1013,6 +1014,19 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) } +static int nl80211_get_sta_mlo_info(void *priv, + struct driver_sta_mlo_info *mlo_info) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!drv->associated) + return -1; + + os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info)); + return 0; +} + static void wpa_driver_nl80211_event_newlink( struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, int ifindex, const char *ifname) @@ -1447,9 +1461,11 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + [NL80211_BSS_MLO_LINK_ID] = { .type = NLA_U8 }, }; struct nl80211_get_assoc_freq_arg *ctx = arg; enum nl80211_bss_status status; + struct wpa_driver_nl80211_data *drv = ctx->drv; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -1461,7 +1477,11 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) status = nla_get_u32(bss[NL80211_BSS_STATUS]); if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_FREQUENCY]) { + bss[NL80211_BSS_FREQUENCY] && + (!drv->sta_mlo_info.valid_links || + (bss[NL80211_BSS_MLO_LINK_ID] && + drv->mlo_assoc_link_id == + nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID])))) { ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", ctx->assoc_freq); @@ -1473,7 +1493,11 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) ctx->ibss_freq); } if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_BSSID]) { + bss[NL80211_BSS_BSSID] && + (!drv->sta_mlo_info.valid_links || + (bss[NL80211_BSS_MLO_LINK_ID] && + drv->mlo_assoc_link_id == + nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID])))) { os_memcpy(ctx->assoc_bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); wpa_printf(MSG_DEBUG, "nl80211: Associated with " @@ -6395,6 +6419,17 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) return -1; + /* + * Indicate driver's SME to enable MLO connection only for non-WPA open + * connections. + */ + if ((!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) && + params->key_mgmt_suite == WPA_KEY_MGMT_NONE && + params->pairwise_suite == WPA_CIPHER_NONE && + params->group_suite == WPA_CIPHER_NONE && + nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT)) + return -1; + return 0; } @@ -6861,14 +6896,16 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) struct nl_msg *msg; struct nl80211_sta_flag_update upd; int ret; + const u8 *connected_addr = drv->sta_mlo_info.valid_links ? + drv->sta_mlo_info.ap_mld_addr : drv->bssid; - if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) { + if (!drv->associated && is_zero_ether_addr(connected_addr) && !authorized) { wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated"); return 0; } wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for " - MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid)); + MACSTR, authorized ? "" : "un", MAC2STR(connected_addr)); os_memset(&upd, 0, sizeof(upd)); upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED); @@ -6876,7 +6913,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) || nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) { nlmsg_free(msg); return -ENOBUFS; @@ -9742,6 +9779,38 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) return pos - buf; pos += res; + if (drv->sta_mlo_info.valid_links) { + int i; + struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info; + + res = os_snprintf(pos, end - pos, + "ap_mld_addr=" MACSTR "\n", + MAC2STR(mlo->ap_mld_addr)); + + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(mlo->valid_links & BIT(i))) + continue; + + res = os_snprintf(pos, end - pos, + "link_addr[%u]=" MACSTR "\n" + "link_bssid[%u]=" MACSTR "\n", + i, + MAC2STR(mlo->links[i].addr), + i, + MAC2STR(mlo->links[i].bssid)); + + if (os_snprintf_error(end - pos, res)) + return pos - buf; + + pos += res; + } + } + if (drv->has_capability) { res = os_snprintf(pos, end - pos, "capa.key_mgmt=0x%x\n" @@ -12482,4 +12551,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .register_frame = testing_nl80211_register_frame, .radio_disable = testing_nl80211_radio_disable, #endif /* CONFIG_TESTING_OPTIONS */ + .get_sta_mlo_info = nl80211_get_sta_mlo_info, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 80d4564..f359663 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -128,6 +128,8 @@ struct wpa_driver_nl80211_data { u8 bssid[ETH_ALEN]; u8 prev_bssid[ETH_ALEN]; int associated; + int mlo_assoc_link_id; + struct driver_sta_mlo_info sta_mlo_info; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; enum nl80211_iftype nlmode; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 369337f..3183508 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -420,6 +420,71 @@ convert_connect_fail_reason_codes(enum qca_sta_connect_fail_reason_codes #endif /* CONFIG_DRIVER_NL80211_QCA */ +static void nl80211_parse_mlo_info(struct wpa_driver_nl80211_data *drv, + struct nlattr *addr, + struct nlattr *mlo_links, + struct nlattr *resp_ie) +{ + struct nlattr *link; + int rem_links; + const u8 *ml_ie; + struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info; + + if (!addr || !mlo_links || !resp_ie) + return; + + ml_ie = get_ie_ext(nla_data(resp_ie), nla_len(resp_ie), + WLAN_EID_EXT_MULTI_LINK); + +#define ML_IE_SELF_LINK_ID_OFFSET \ + (3 + /* IE header */ \ + 2 + /* Control field */ \ + 1 + /* Common info length field */ \ + 6) /* MLD mac address */ + if (!ml_ie || nla_len(resp_ie) <= ML_IE_SELF_LINK_ID_OFFSET) + return; + + drv->mlo_assoc_link_id = ml_ie[ML_IE_SELF_LINK_ID_OFFSET]; + os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN); + wpa_printf(MSG_DEBUG, "ap_mld_addr " MACSTR, MAC2STR(mlo->ap_mld_addr)); + + nla_for_each_nested(link, mlo_links, rem_links) { + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + int link_id; + + nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link), + NULL); + + if (!tb[NL80211_ATTR_MLO_LINK_ID] || !tb[NL80211_ATTR_MAC] || + !tb[NL80211_ATTR_BSSID]) + continue; + + link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]); + mlo->valid_links |= BIT(link_id); + os_memcpy(mlo->links[link_id].addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + os_memcpy(mlo->links[link_id].bssid, + nla_data(tb[NL80211_ATTR_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "link[%u].addr " MACSTR, link_id, + MAC2STR(mlo->links[link_id].addr)); + wpa_printf(MSG_DEBUG, "link[%u].bssid " MACSTR, link_id, + MAC2STR(mlo->links[link_id].bssid)); + } + + if (drv->mlo_assoc_link_id >= MAX_NUM_MLD_LINKS || + !(mlo->valid_links & BIT(drv->mlo_assoc_link_id))) { + wpa_printf(MSG_ERROR, "Invalid MLO assoc link ID %d", + drv->mlo_assoc_link_id); + mlo->valid_links = 0; + return; + } + + os_memcpy(drv->bssid, + mlo->links[drv->mlo_assoc_link_id].bssid, ETH_ALEN); + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); +} + + static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *status, struct nlattr *addr, struct nlattr *req_ie, @@ -433,7 +498,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *subnet_status, struct nlattr *fils_erp_next_seq_num, struct nlattr *fils_pmk, - struct nlattr *fils_pmkid) + struct nlattr *fils_pmkid, + struct nlattr *mlo_links) { union wpa_event_data event; const u8 *ssid = NULL; @@ -525,7 +591,9 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, } drv->associated = 1; - if (addr) { + drv->sta_mlo_info.valid_links = 0; + nl80211_parse_mlo_info(drv, addr, mlo_links, resp_ie); + if (!drv->sta_mlo_info.valid_links && addr) { os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); } @@ -2111,7 +2179,8 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK], - tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]); + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID], + NULL); } @@ -3113,7 +3182,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, NULL, tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM], tb[NL80211_ATTR_PMK], - tb[NL80211_ATTR_PMKID]); + tb[NL80211_ATTR_PMKID], + tb[NL80211_ATTR_MLO_LINKS]); break; case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: mlme_event_ch_switch(drv, diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index eb97a61..7dcdb99 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -379,6 +379,8 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { + int i; + if (bss == wpa_s->current_bss) return 1; @@ -388,9 +390,23 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) bss->ssid_len) != 0)) return 0; /* SSID has changed */ - return !is_zero_ether_addr(bss->bssid) && - (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0); + if (!is_zero_ether_addr(bss->bssid) && + (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0)) + return 1; + + if (!wpa_s->valid_links) + return 0; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(wpa_s->valid_links & BIT(i))) + continue; + + if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0) + return 1; + } + + return 0; } diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index b0af1cd..e202590 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -1117,4 +1117,14 @@ static inline int wpa_drv_dpp_listen(struct wpa_supplicant *wpa_s, bool enable) return wpa_s->driver->dpp_listen(wpa_s->drv_priv, enable); } +static inline int +wpas_drv_get_sta_mlo_info(struct wpa_supplicant *wpa_s, + struct driver_sta_mlo_info *mlo_info) +{ + if (!wpa_s->driver->get_sta_mlo_info) + return 0; + + return wpa_s->driver->get_sta_mlo_info(wpa_s->drv_priv, mlo_info); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index ec56cfd..0eab93d 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -166,6 +166,20 @@ wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s, const u8 *bssid) return bss; } +static void wpa_supplicant_update_link_bss(struct wpa_supplicant *wpa_s, + u8 link_id, + const u8 *bssid) +{ + struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid); + + if (!bss) { + wpa_supplicant_update_scan_results(wpa_s); + bss = wpa_supplicant_get_new_bss(wpa_s, bssid); + } + + if (bss) + wpa_s->links[link_id].bss = bss; +} static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { @@ -285,6 +299,19 @@ void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) } +static void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s) +{ + int i; + + if (!wpa_s->valid_links) + return; + + wpa_s->valid_links = 0; + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) + wpa_s->links[i].bss = NULL; +} + + void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) { int bssid_changed; @@ -351,6 +378,8 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) wpa_s->enabled_4addr_mode = 0; + + wpas_reset_mlo_info(wpa_s); } @@ -3302,6 +3331,62 @@ static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s, } +static int wpas_update_mlo_info(struct wpa_supplicant *wpa_s) +{ + struct driver_sta_mlo_info mlo; + int i; + + mlo.valid_links = 0; + if (wpas_drv_get_sta_mlo_info(wpa_s, &mlo)) { + wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return -1; + } + + if (wpa_s->valid_links == mlo.valid_links) { + bool match = true; + + if (!mlo.valid_links) + return 0; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(mlo.valid_links & BIT(i))) + continue; + + if (os_memcmp(wpa_s->links[i].addr, mlo.links[i].addr, + ETH_ALEN)) { + match = false; + break; + } + + if (os_memcmp(wpa_s->links[i].bssid, mlo.links[i].bssid, + ETH_ALEN)) { + match = false; + break; + } + } + + if (match && + !os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN)) + return 0; + } + + wpa_s->valid_links = mlo.valid_links; + os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN); + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(wpa_s->valid_links & BIT(i))) + continue; + + os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN); + os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN); + wpa_supplicant_update_link_bss(wpa_s, i, mlo.links[i].bssid); + } + + return 0; +} + + static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -3402,6 +3487,10 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, "WPA/RSN IEs not updated"); } + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpas_update_mlo_info(wpa_s) < 0) + return; + wpas_fst_update_mb_assoc(wpa_s, data); #ifdef CONFIG_SME diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index a349d65..23d418e 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -739,6 +739,13 @@ struct wpa_supplicant { struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; + u8 ap_mld_addr[ETH_ALEN]; + u8 valid_links; + struct { + u8 addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + } links[MAX_NUM_MLD_LINKS]; u8 *last_con_fail_realm; size_t last_con_fail_realm_len; -- 2.7.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap