diff options
Diffstat (limited to 'src/drivers/driver_nl80211.c')
-rw-r--r-- | src/drivers/driver_nl80211.c | 1810 |
1 files changed, 1530 insertions, 280 deletions
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index a2c48423..cc87e72d 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -31,6 +31,8 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_common.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" #include "netlink.h" #include "linux_defines.h" #include "linux_ioctl.h" @@ -38,9 +40,9 @@ #include "radiotap_iter.h" #include "rfkill.h" #include "driver_nl80211.h" -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) #include "common/brcm_vendor.h" -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ #ifndef NETLINK_CAP_ACK #define NETLINK_CAP_ACK 10 @@ -173,6 +175,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, const u16 *csa_offs, size_t csa_offs_len); static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report); +static int nl80211_put_freq_params(struct nl_msg *msg, + const struct hostapd_freq_params *freq); #define IFIDX_ANY -1 @@ -199,9 +203,9 @@ static int nl80211_put_mesh_config(struct nl_msg *msg, #endif /* CONFIG_MESH */ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, u16 reason); -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) static int nl80211_set_td_policy(void *priv, u32 td_policy); -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ /* Converts nl80211_chan_width to a common format */ enum chan_width convert2width(int width) @@ -219,8 +223,11 @@ enum chan_width convert2width(int width) return CHAN_WIDTH_80P80; case NL80211_CHAN_WIDTH_160: return CHAN_WIDTH_160; + case NL80211_CHAN_WIDTH_320: + return CHAN_WIDTH_320; + default: + return CHAN_WIDTH_UNKNOWN; } - return CHAN_WIDTH_UNKNOWN; } @@ -270,8 +277,17 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) if (drv->associated) os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); drv->associated = 0; + os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info)); os_memset(drv->bssid, 0, ETH_ALEN); - drv->first_bss->freq = 0; + drv->first_bss->flink->freq = 0; +#ifdef CONFIG_DRIVER_NL80211_QCA + os_free(drv->pending_roam_data); + drv->pending_roam_data = NULL; +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + drv->auth_mld = false; + drv->auth_mld_link_id = -1; + os_memset(drv->auth_ap_mld_addr, 0, ETH_ALEN); } @@ -901,7 +917,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) dl_list_init(&w->drvs); /* Beacon frames not supported in IEEE 802.11ad */ - if (ieee80211_freq_to_chan(bss->freq, &channel) != + if (ieee80211_freq_to_chan(bss->flink->freq, &channel) != HOSTAPD_MODE_IEEE80211AD) { w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (!w->nl_cb) { @@ -1015,6 +1031,74 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) } +static int get_mlo_info(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + 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 }, + [NL80211_ATTR_MAC] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN }, + }; + struct driver_sta_mlo_info *info = arg; + int rem; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_MLO_LINKS]) + return NL_SKIP; + + info->valid_links = 0; + 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] || + !link_data[NL80211_ATTR_MAC]) + continue; + + link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]); + if (link_id >= MAX_NUM_MLD_LINKS) + continue; + info->valid_links |= BIT(link_id); + os_memcpy(info->links[link_id].addr, + nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN); + if (link_data[NL80211_ATTR_WIPHY_FREQ]) + info->links[link_id].freq = + nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]); + } + + return NL_SKIP; +} + + +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; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + if (send_and_recv_msgs(drv, msg, get_mlo_info, + &drv->sta_mlo_info, NULL, NULL)) + 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) @@ -1169,6 +1253,7 @@ static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv, MACSTR " to " MACSTR, ifindex, bss->ifname, MAC2STR(bss->addr), MAC2STR(addr)); + os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN); os_memcpy(bss->addr, addr, ETH_ALEN); if (notify) wpa_supplicant_event(drv->ctx, @@ -1440,6 +1525,8 @@ struct nl80211_get_assoc_freq_arg { u8 assoc_bssid[ETH_ALEN]; u8 assoc_ssid[SSID_MAX_LEN]; u8 assoc_ssid_len; + u8 bssid[MAX_NUM_MLD_LINKS][ETH_ALEN]; + unsigned int freq[MAX_NUM_MLD_LINKS]; }; static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) @@ -1452,9 +1539,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); @@ -1467,9 +1556,25 @@ 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]) { - ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", - ctx->assoc_freq); + int link_id = -1; + u32 freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + + if (bss[NL80211_BSS_MLO_LINK_ID]) + link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]); + + if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) { + ctx->freq[link_id] = freq; + wpa_printf(MSG_DEBUG, + "nl80211: MLO link %d associated on %u MHz", + link_id, ctx->freq[link_id]); + } + + if (!drv->sta_mlo_info.valid_links || + drv->sta_mlo_info.assoc_link_id == link_id) { + ctx->assoc_freq = freq; + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + ctx->assoc_freq); + } } if (status == NL80211_BSS_STATUS_IBSS_JOINED && bss[NL80211_BSS_FREQUENCY]) { @@ -1479,10 +1584,26 @@ static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) } if (status == NL80211_BSS_STATUS_ASSOCIATED && bss[NL80211_BSS_BSSID]) { - os_memcpy(ctx->assoc_bssid, - nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - wpa_printf(MSG_DEBUG, "nl80211: Associated with " - MACSTR, MAC2STR(ctx->assoc_bssid)); + int link_id = -1; + const u8 *bssid = nla_data(bss[NL80211_BSS_BSSID]); + + if (bss[NL80211_BSS_MLO_LINK_ID]) + link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]); + + if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) { + os_memcpy(ctx->bssid[link_id], bssid, ETH_ALEN); + wpa_printf(MSG_DEBUG, + "nl80211: MLO link %d associated with " + MACSTR, link_id, MAC2STR(bssid)); + } + + if (!drv->sta_mlo_info.valid_links || + drv->sta_mlo_info.assoc_link_id == link_id) { + os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(bssid)); + } + } if (status == NL80211_BSS_STATUS_ASSOCIATED && @@ -1568,94 +1689,21 @@ try_again: "associated BSS from scan results: %u MHz", freq); if (freq) drv->assoc_freq = freq; - return drv->assoc_freq; - } - wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " - "(%s)", ret, strerror(-ret)); - return drv->assoc_freq; -} + if (drv->sta_mlo_info.valid_links) { + int i; -static int get_link_signal(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; - static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { - [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, - [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, - [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 }, - }; - struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; - static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { - [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, - [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, - [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, - [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, - }; - struct wpa_signal_info *sig_change = arg; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (!tb[NL80211_ATTR_STA_INFO] || - nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, - tb[NL80211_ATTR_STA_INFO], policy)) - return NL_SKIP; - if (!sinfo[NL80211_STA_INFO_SIGNAL]) - return NL_SKIP; - - sig_change->current_signal = - (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); - - if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) - sig_change->avg_signal = - (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]); - else - sig_change->avg_signal = 0; - - if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]) - sig_change->avg_beacon_signal = - (s8) - nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]); - else - sig_change->avg_beacon_signal = 0; - - if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { - if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, - sinfo[NL80211_STA_INFO_TX_BITRATE], - rate_policy)) { - sig_change->current_txrate = 0; - } else { - if (rinfo[NL80211_RATE_INFO_BITRATE]) { - sig_change->current_txrate = - nla_get_u16(rinfo[ - NL80211_RATE_INFO_BITRATE]) * 100; - } + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) + drv->sta_mlo_info.links[i].freq = arg.freq[i]; } - } - return NL_SKIP; -} - - -int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, - struct wpa_signal_info *sig) -{ - struct nl_msg *msg; - - sig->current_signal = -WPA_INVALID_NOISE; - sig->current_txrate = 0; - - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) { - nlmsg_free(msg); - return -ENOBUFS; + return drv->assoc_freq; } - - return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL); + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); + return drv->assoc_freq; } - static int get_link_noise(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1858,6 +1906,16 @@ static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) { + /* put wiphy idx to get the interface specific country code + * instead of the global one. */ + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) { + nlmsg_free(msg); + return -1; + } + } + alpha2[0] = '\0'; ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2, NULL, NULL); @@ -2190,6 +2248,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, { struct wpa_driver_nl80211_data *drv; struct i802_bss *bss; + unsigned int i; if (global_priv == NULL) return NULL; @@ -2268,6 +2327,17 @@ skip_wifi_status: drv->in_interface_list = 1; } + /* + * Set the default link to be the first one, and set its address to that + * of the interface. + */ + bss->flink = &bss->links[0]; + bss->n_links = 1; + os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN); + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) + bss->links[i].link_id = NL80211_DRV_LINK_ID_NA; + return bss; failed: @@ -2385,8 +2455,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) #ifdef CONFIG_PASN /* register for PASN Authentication frames */ - if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && - nl80211_register_frame(bss, bss->nl_mgmt, type, + if (nl80211_register_frame(bss, bss->nl_mgmt, type, (u8 *) "\x07\x00", 2, false)) ret = -1; #endif /* CONFIG_PASN */ @@ -2936,19 +3005,50 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, } -static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss) +static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss, + struct i802_link *link) { struct nl_msg *msg; struct wpa_driver_nl80211_data *drv = bss->drv; + if (!link->beacon_set) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", drv->ifindex); + link->beacon_set = 0; + link->freq = 0; + nl80211_put_wiphy_data_ap(bss); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON); + if (!msg) + return -ENOBUFS; + + if (link->link_id != NL80211_DRV_LINK_ID_NA) { + wpa_printf(MSG_DEBUG, + "nl80211: MLD: stop beaconing on link=%u", + link->link_id); + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + link->link_id)) { + nlmsg_free(msg); + return -ENOBUFS; + } + } + return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); } +static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss) +{ + unsigned int i; + + for (i = 0; i < bss->n_links; i++) + wpa_driver_nl80211_del_beacon(bss, &bss->links[i]); +} + + /** * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init() @@ -2998,7 +3098,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_remove_monitor_interface(drv); if (is_ap_interface(drv->nlmode)) - wpa_driver_nl80211_del_beacon(bss); + wpa_driver_nl80211_del_beacon_all(bss); if (drv->eapol_sock >= 0) { eloop_unregister_read_sock(drv->eapol_sock); @@ -3128,9 +3228,9 @@ static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) return RSN_CIPHER_SUITE_WEP40; case WPA_CIPHER_GTK_NOT_USED: return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; + default: + return 0; } - - return 0; } @@ -3174,7 +3274,9 @@ static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[], __AKM(IEEE8021X_SHA256, 802_1X_SHA256); __AKM(PSK_SHA256, PSK_SHA256); __AKM(SAE, SAE); + __AKM(SAE_EXT_KEY, SAE_EXT_KEY); __AKM(FT_SAE, FT_SAE); + __AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY); __AKM(CCKM, CCKM); __AKM(OSEN, OSEN); __AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B); @@ -3191,7 +3293,7 @@ static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[], return num_suites; } -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv, struct drv_acs_params *params) { @@ -3241,9 +3343,9 @@ fail: nlmsg_free(msg); return ret; } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) static int wpa_cross_akm_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[], int max_suites) { @@ -3259,7 +3361,7 @@ static int wpa_cross_akm_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 su return num_suites; } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ #ifdef CONFIG_DRIVER_NL80211_QCA static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, @@ -3292,7 +3394,7 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, #endif /* CONFIG_DRIVER_NL80211_QCA */ -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) static int key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, const u8 *key, size_t key_len) { @@ -3320,7 +3422,8 @@ static int key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, return ret; } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ + static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv, const u8 *key, size_t key_len, @@ -3380,6 +3483,7 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, size_t key_len = params->key_len; int vlan_id = params->vlan_id; enum key_flag key_flag = params->key_flag; + int link_id = params->link_id; /* Ignore for P2P Device */ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) @@ -3387,9 +3491,10 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, ifindex = if_nametoindex(ifname); wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " - "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x", + "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d", __func__, ifindex, ifname, alg, addr, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len, key_flag); + (unsigned long) seq_len, (unsigned long) key_len, key_flag, + link_id); if (check_key_flag(key_flag)) { wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__); @@ -3409,12 +3514,13 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, if (key_flag & KEY_FLAG_PMK) { if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) return nl80211_set_pmk(drv, key, key_len, addr); -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) if (drv->vendor_set_pmk) { wpa_printf(MSG_INFO, "nl80211: key_mgmt_set_key with key_len %lu", (unsigned long) key_len); return key_mgmt_set_key(drv, key, key_len); } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ + /* The driver does not have any offload mechanism for PMK, so * there is no need to configure this key. */ return 0; @@ -3522,6 +3628,12 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, goto fail; } + if (link_id != -1) { + wpa_printf(MSG_DEBUG, "nl80211: Link ID %d", link_id); + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) ret = 0; @@ -3584,6 +3696,13 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, goto fail; } + if (link_id != -1) { + wpa_printf(MSG_DEBUG, "nl80211: set_key default - Link ID %d", + link_id); + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret) wpa_printf(MSG_DEBUG, @@ -3807,6 +3926,15 @@ static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, } } + if (params->mld && params->ap_mld_addr) { + drv->auth_mld = params->mld; + drv->auth_mld_link_id = params->mld_link_id; + os_memcpy(drv->auth_ap_mld_addr, params->ap_mld_addr, ETH_ALEN); + } else { + drv->auth_mld = false; + drv->auth_mld_link_id = -1; + } + os_free(drv->auth_data); drv->auth_data = NULL; drv->auth_data_len = 0; @@ -3911,6 +4039,7 @@ retry: os_memset(&p, 0, sizeof(p)); p.ifname = bss->ifname; p.alg = WPA_ALG_WEP; + p.link_id = -1; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; @@ -3969,6 +4098,17 @@ retry: goto fail; } + if (params->mld && params->ap_mld_addr) { + wpa_printf(MSG_DEBUG, " * MLD: link_id=%u, MLD addr=" MACSTR, + params->mld_link_id, MAC2STR(params->ap_mld_addr)); + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + params->mld_link_id) || + nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, + params->ap_mld_addr)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); msg = NULL; if (ret) { @@ -4072,6 +4212,10 @@ int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv) params.ie_len = drv->auth_ie_len; params.auth_data = drv->auth_data; params.auth_data_len = drv->auth_data_len; + params.mld = drv->auth_mld; + params.mld_link_id = drv->auth_mld_link_id; + if (drv->auth_mld) + params.ap_mld_addr = drv->auth_ap_mld_addr; for (i = 0; i < 4; i++) { if (drv->auth_wep_key_len[i]) { @@ -4131,10 +4275,10 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { if (freq == 0) { wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d", - bss->freq); - freq = bss->freq; + bss->flink->freq); + freq = bss->flink->freq; } - if ((int) freq == bss->freq) + if ((int) freq == bss->flink->freq) wait_time = 0; goto send_frame_cmd; } @@ -4196,14 +4340,14 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, } if (freq == 0) { wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u", - bss->freq); - freq = bss->freq; + bss->flink->freq); + freq = bss->flink->freq; } if (drv->use_monitor && is_ap_interface(drv->nlmode)) { wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor", - freq, bss->freq); + freq, bss->flink->freq); return nl80211_send_monitor(drv, data, data_len, encrypt, noack); } @@ -4559,18 +4703,18 @@ static int nl80211_set_multicast_to_unicast(struct i802_bss *bss, #ifdef CONFIG_SAE -static int nl80211_put_sae_pwe(struct nl_msg *msg, int pwe) +static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe) { u8 sae_pwe; wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe); - if (pwe == 0) + if (pwe == SAE_PWE_HUNT_AND_PECK) sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK; - else if (pwe == 1) + else if (pwe == SAE_PWE_HASH_TO_ELEMENT) sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT; - else if (pwe == 2) + else if (pwe == SAE_PWE_BOTH) sae_pwe = NL80211_SAE_PWE_BOTH; - else if (pwe == 3) + else if (pwe == SAE_PWE_FORCE_HUNT_AND_PECK) return 0; /* special test mode */ else return -1; @@ -4613,6 +4757,7 @@ static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg, #ifdef CONFIG_IEEE80211AX + static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss, struct nl_msg *msg, struct wpa_driver_ap_params *params) @@ -4642,6 +4787,60 @@ static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss, nla_nest_end(msg, attr); return 0; } + + +static int nl80211_mbssid(struct nl_msg *msg, + struct wpa_driver_ap_params *params) +{ + struct nlattr *config, *elems; + int ifidx; + + if (!params->mbssid_tx_iface) + return 0; + + config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG); + if (!config || + nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX, + params->mbssid_index)) + return -1; + + if (params->mbssid_tx_iface) { + ifidx = if_nametoindex(params->mbssid_tx_iface); + if (ifidx <= 0 || + nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX, + ifidx)) + return -1; + } + + if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA)) + return -1; + + nla_nest_end(msg, config); + + if (params->mbssid_elem_count && params->mbssid_elem_len && + params->mbssid_elem_offset && *params->mbssid_elem_offset) { + u8 i, **offs = params->mbssid_elem_offset; + + elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS); + if (!elems) + return -1; + + for (i = 0; i < params->mbssid_elem_count - 1; i++) { + if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i])) + return -1; + } + + if (nla_put(msg, i + 1, + *offs + params->mbssid_elem_len - offs[i], + offs[i])) + return -1; + + nla_nest_end(msg, elems); + } + + return 0; +} + #endif /* CONFIG_IEEE80211AX */ @@ -4661,7 +4860,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_mesh_bss_params mesh_params; #endif /* CONFIG_MESH */ - beacon_set = params->reenable ? 0 : bss->beacon_set; + beacon_set = params->reenable ? 0 : bss->flink->beacon_set; wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", beacon_set); @@ -4768,10 +4967,25 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) goto fail; - if (drv->device_ap_sme && - (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) && - nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) - goto fail; + if (drv->device_ap_sme) { + u32 flags = 0; + + if (params->key_mgmt_suites & (WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_SAE_EXT_KEY)) { + /* Add the previously used flag attribute to support + * older kernel versions and the newer flag bit for + * newer kernels. */ + if (nla_put_flag(msg, + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) + goto fail; + flags |= NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT; + } + + flags |= NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT; + + if (nla_put_u32(msg, NL80211_ATTR_AP_SETTINGS_FLAGS, flags)) + goto fail; + } wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", params->pairwise_ciphers); @@ -4900,6 +5114,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_nest_end(msg, spr); } + if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0) + goto fail; + if (params->freq && params->freq->he_enabled) { struct nlattr *bss_color; @@ -4925,11 +5142,13 @@ static int wpa_driver_nl80211_set_ap(void *priv, if (params->unsol_bcast_probe_resp_interval && nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0) goto fail; + + if (nl80211_mbssid(msg, params) < 0) + goto fail; #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_SAE - if (((params->key_mgmt_suites & WPA_KEY_MGMT_SAE) || - (params->key_mgmt_suites & WPA_KEY_MGMT_FT_SAE)) && + if (wpa_key_mgmt_sae(params->key_mgmt_suites) && nl80211_put_sae_pwe(msg, params->sae_pwe) < 0) goto fail; #endif /* CONFIG_SAE */ @@ -4939,22 +5158,30 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; #endif /* CONFIG_FILS */ + if (params->punct_bitmap) { + wpa_printf(MSG_DEBUG, "nl80211: Puncturing bitmap=0x%04x", + params->punct_bitmap); + if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, + params->punct_bitmap)) + goto fail; + } + ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", ret, strerror(-ret)); } else { - bss->beacon_set = 1; + bss->flink->beacon_set = 1; nl80211_set_bss(bss, params->cts_protect, params->preamble, params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); nl80211_set_multicast_to_unicast(bss, params->multicast_to_unicast); if (beacon_set && params->freq && - params->freq->bandwidth != bss->bandwidth) { + params->freq->bandwidth != bss->flink->bandwidth) { wpa_printf(MSG_DEBUG, "nl80211: Update BSS %s bandwidth: %d -> %d", - bss->ifname, bss->bandwidth, + bss->ifname, bss->flink->bandwidth, params->freq->bandwidth); ret = nl80211_set_channel(bss, params->freq, 1); if (ret) { @@ -4964,7 +5191,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, } else { wpa_printf(MSG_DEBUG, "nl80211: Frequency set succeeded for ht2040 coex"); - bss->bandwidth = params->freq->bandwidth; + bss->flink->bandwidth = params->freq->bandwidth; } } else if (!beacon_set && params->freq) { /* @@ -4972,7 +5199,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, * mode only at the point when beaconing is started, so * set the initial value here. */ - bss->bandwidth = params->freq->bandwidth; + bss->flink->bandwidth = params->freq->bandwidth; } } @@ -5005,15 +5232,19 @@ static int nl80211_put_freq_params(struct nl_msg *msg, if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq)) return -ENOBUFS; + wpa_printf(MSG_DEBUG, " * eht_enabled=%d", freq->eht_enabled); wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled); wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled); wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled); + wpa_printf(MSG_DEBUG, " * radar_background=%d", + freq->radar_background); hw_mode = ieee80211_freq_to_chan(freq->freq, &channel); is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G || hw_mode == HOSTAPD_MODE_IEEE80211B; - if (freq->vht_enabled || (freq->he_enabled && !is_24ghz)) { + if (freq->vht_enabled || + ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) { enum nl80211_chan_width cw; wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth); @@ -5033,6 +5264,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg, case 160: cw = NL80211_CHAN_WIDTH_160; break; + case 320: + cw = NL80211_CHAN_WIDTH_320; + break; default: return -EINVAL; } @@ -5085,6 +5319,10 @@ static int nl80211_put_freq_params(struct nl_msg *msg, NL80211_CHAN_NO_HT)) return -ENOBUFS; } + if (freq->radar_background && + nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND)) + return -ENOBUFS; + return 0; } @@ -5097,9 +5335,10 @@ static int nl80211_set_channel(struct i802_bss *bss, int ret; wpa_printf(MSG_DEBUG, - "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", - freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled, - freq->bandwidth, freq->center_freq1, freq->center_freq2); + "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->he_enabled, freq->eht_enabled, freq->bandwidth, + freq->center_freq1, freq->center_freq2); msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL : NL80211_CMD_SET_WIPHY); @@ -5110,7 +5349,7 @@ static int nl80211_set_channel(struct i802_bss *bss, ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret == 0) { - bss->freq = freq->freq; + bss->flink->freq = freq->freq; return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " @@ -5177,16 +5416,29 @@ static int wpa_driver_nl80211_sta_add(void *priv, struct nl_msg *msg; struct nl80211_sta_flag_update upd; int ret = -ENOBUFS; + u8 cmd; + const char *cmd_string; if ((params->flags & WPA_STA_TDLS_PEER) && !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; + if (params->mld_link_sta) { + cmd = params->set ? NL80211_CMD_MODIFY_LINK_STA : + NL80211_CMD_ADD_LINK_STA; + cmd_string = params->set ? "NL80211_CMD_MODIFY_LINK_STA" : + "NL80211_CMD_ADD_LINK_STA"; + } else { + cmd = params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION; + cmd_string = params->set ? "NL80211_CMD_SET_STATION" : + "NL80211_CMD_NEW_STATION"; + } + wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, - params->set ? "Set" : "Add", MAC2STR(params->addr)); - msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION : - NL80211_CMD_NEW_STATION); - if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr)) + cmd_string, MAC2STR(params->addr)); + msg = nl80211_bss_msg(bss, 0, cmd); + if (!msg) goto fail; /* @@ -5248,6 +5500,14 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } + if (params->eht_capab) { + wpa_hexdump(MSG_DEBUG, " * eht_capab", + params->eht_capab, params->eht_capab_len); + if (nla_put(msg, NL80211_ATTR_EHT_CAPABILITY, + params->eht_capab_len, params->eht_capab)) + goto fail; + } + if (params->ext_capab) { wpa_hexdump(MSG_DEBUG, " * ext_capab", params->ext_capab, params->ext_capab_len); @@ -5396,12 +5656,43 @@ static int wpa_driver_nl80211_sta_add(void *priv, nla_nest_end(msg, wme); } + /* In case we are an AP MLD need to always specify the link ID */ + if (params->mld_link_id >= 0) { + wpa_printf(MSG_DEBUG, " * mld_link_id=%d", + params->mld_link_id); + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + params->mld_link_id)) + goto fail; + + /* + * If the link address is specified the station is a non-AP MLD + * and thus need to provide the MLD address as the station + * address, and the non-AP MLD link address as the link address. + */ + if (params->mld_link_addr) { + wpa_printf(MSG_DEBUG, " * mld_link_addr=" MACSTR, + MAC2STR(params->mld_link_addr)); + + if (nla_put(msg, NL80211_ATTR_MLD_ADDR, + ETH_ALEN, params->addr) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + params->mld_link_addr)) + goto fail; + } else { + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + params->addr)) + goto fail; + } + } else { + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); msg = NULL; if (ret) - wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " - "result: %d (%s)", params->set ? "SET" : "NEW", ret, - strerror(-ret)); + wpa_printf(MSG_DEBUG, "nl80211: %s result: %d (%s)", + cmd_string, ret, strerror(-ret)); if (ret == -EEXIST) ret = 0; fail: @@ -5730,7 +6021,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss) nl80211_mgmt_unsubscribe(bss, "AP teardown"); nl80211_put_wiphy_data_ap(bss); - bss->beacon_set = 0; + bss->flink->beacon_set = 0; } @@ -6066,6 +6357,12 @@ static int nl80211_ht_vht_overrides(struct nl_msg *msg, } #endif /* CONFIG_HE_OVERRIDES */ + if (params->disable_eht) { + wpa_printf(MSG_DEBUG, " * EHT disabled"); + if (nla_put_flag(msg, NL80211_ATTR_DISABLE_EHT)) + return -1; + } + return 0; } @@ -6205,14 +6502,100 @@ static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv, } +static unsigned int num_bits_set(u32 val) +{ + unsigned int c; + + for (c = 0; val; c++) + val &= val - 1; + + return c; +} + + static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_msg *msg) { + if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) { + struct wpa_driver_mld_params *mld_params = ¶ms->mld_params; + struct nlattr *links, *attr; + int i; + u8 link_id; + + wpa_printf(MSG_DEBUG, " * MLD: MLD addr=" MACSTR, + MAC2STR(mld_params->mld_addr)); + + if (nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, + mld_params->mld_addr) || + nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + mld_params->assoc_link_id)) + return -1; + + links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); + if (!links) + return -1; + + attr = nla_nest_start(msg, 0); + if (!attr) + return -1; + + /* First add the association link ID */ + link_id = mld_params->assoc_link_id; + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + mld_params->mld_links[link_id].bssid) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + mld_params->mld_links[link_id].freq)) + return -1; + + os_memcpy(drv->sta_mlo_info.links[link_id].bssid, + mld_params->mld_links[link_id].bssid, ETH_ALEN); + + nla_nest_end(msg, attr); + + for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS; + link_id++) { + if (!(mld_params->valid_links & BIT(link_id)) || + link_id == mld_params->assoc_link_id) + continue; + + attr = nla_nest_start(msg, i); + if (!attr) + return -1; + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + link_id) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + mld_params->mld_links[link_id].bssid) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + mld_params->mld_links[link_id].freq) || + (mld_params->mld_links[link_id].ies && + mld_params->mld_links[i].ies_len && + nla_put(msg, NL80211_ATTR_IE, + mld_params->mld_links[link_id].ies_len, + mld_params->mld_links[link_id].ies))) + return -1; + + os_memcpy(drv->sta_mlo_info.links[link_id].bssid, + mld_params->mld_links[link_id].bssid, + ETH_ALEN); + nla_nest_end(msg, attr); + i++; + } + + nla_nest_end(msg, links); + + os_memcpy(drv->sta_mlo_info.ap_mld_addr, + params->mld_params.mld_addr, ETH_ALEN); + drv->sta_mlo_info.assoc_link_id = mld_params->assoc_link_id; + drv->sta_mlo_info.req_links = mld_params->valid_links; + } + if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) return -1; - if (params->bssid) { + if (params->bssid && !params->mld_params.mld_addr) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) @@ -6227,7 +6610,7 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } - if (params->freq.freq) { + if (params->freq.freq && !params->mld_params.mld_addr) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq.freq)) @@ -6324,7 +6707,9 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_SAE || + params->key_mgmt_suite == WPA_KEY_MGMT_SAE_EXT_KEY || params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE_EXT_KEY || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 || params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || @@ -6334,74 +6719,114 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_OWE || params->key_mgmt_suite == WPA_KEY_MGMT_DPP) { - int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + u32 *mgmt; + unsigned int akm_count = 1, i; + + /* + * Make sure the driver has capability to handle default AKM in + * key_mgmt_suite plus allowed AKMs in allowed_key_mgmts. + */ + if (drv->capa.max_num_akms <= + num_bits_set(params->allowed_key_mgmts)) { + wpa_printf(MSG_INFO, + "nl80211: Not enough support for the allowed AKMs (max_num_akms=%u <= num_bits_set=%u)", + drv->capa.max_num_akms, + num_bits_set(params->allowed_key_mgmts)); + return -1; + } + + mgmt = os_malloc(sizeof(u32) * drv->capa.max_num_akms); + if (!mgmt) + return -1; + + mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; switch (params->key_mgmt_suite) { case WPA_KEY_MGMT_CCKM: - mgmt = RSN_AUTH_KEY_MGMT_CCKM; + mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM; break; case WPA_KEY_MGMT_IEEE8021X: - mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; break; case WPA_KEY_MGMT_FT_IEEE8021X: - mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X; break; case WPA_KEY_MGMT_FT_PSK: - mgmt = RSN_AUTH_KEY_MGMT_FT_PSK; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK; break; case WPA_KEY_MGMT_IEEE8021X_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256; break; case WPA_KEY_MGMT_PSK_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256; break; case WPA_KEY_MGMT_OSEN: - mgmt = RSN_AUTH_KEY_MGMT_OSEN; + mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN; break; case WPA_KEY_MGMT_SAE: - mgmt = RSN_AUTH_KEY_MGMT_SAE; + mgmt[0] = RSN_AUTH_KEY_MGMT_SAE; + break; + case WPA_KEY_MGMT_SAE_EXT_KEY: + mgmt[0] = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY; break; case WPA_KEY_MGMT_FT_SAE: - mgmt = RSN_AUTH_KEY_MGMT_FT_SAE; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE; + break; + case WPA_KEY_MGMT_FT_SAE_EXT_KEY: + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: - mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; + mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: - mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; break; case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: - mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; break; case WPA_KEY_MGMT_FILS_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256; break; case WPA_KEY_MGMT_FILS_SHA384: - mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384; + mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384; break; case WPA_KEY_MGMT_FT_FILS_SHA256: - mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; break; case WPA_KEY_MGMT_FT_FILS_SHA384: - mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; break; case WPA_KEY_MGMT_OWE: - mgmt = RSN_AUTH_KEY_MGMT_OWE; + mgmt[0] = RSN_AUTH_KEY_MGMT_OWE; break; case WPA_KEY_MGMT_DPP: - mgmt = RSN_AUTH_KEY_MGMT_DPP; + mgmt[0] = RSN_AUTH_KEY_MGMT_DPP; break; case WPA_KEY_MGMT_PSK: default: - mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; break; } - wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt); - if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt)) + + if (drv->capa.max_num_akms > 1) { + akm_count += wpa_key_mgmt_to_suites( + params->allowed_key_mgmts, &mgmt[1], + drv->capa.max_num_akms - 1); + } + + for (i = 0; i < akm_count; i++) + wpa_printf(MSG_DEBUG, " * akm[%d]=0x%x", i, mgmt[i]); + + if (nla_put(msg, NL80211_ATTR_AKM_SUITES, + akm_count * sizeof(u32), mgmt)) { + os_free(mgmt); return -1; + } + + os_free(mgmt); } -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) if (IS_CROSS_AKM_ROAM_KEY_MGMT(params->key_mgmt_suite)) { int num_suites; u32 suites[NL80211_MAX_NR_AKM_SUITES]; @@ -6416,7 +6841,7 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ if (params->req_handshake_offload && (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) { wpa_printf(MSG_DEBUG, " * WANT_1X_4WAY_HS"); @@ -6479,17 +6904,21 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, nl80211_put_fils_connect_params(drv, params, msg) != 0) return -1; - if (( -#ifdef CONFIG_DRIVER_NL80211_BRCM - (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) || +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) + if (((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) || + (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) && #else - params->key_mgmt_suite == WPA_KEY_MGMT_SAE || -#endif /* CONFIG_DRIVER_NL80211_BRCM */ - params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) && + if ((wpa_key_mgmt_sae(params->key_mgmt_suite) || + wpa_key_mgmt_sae(params->allowed_key_mgmts)) && +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) && nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) return -1; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT)) + return -1; + return 0; } @@ -6506,9 +6935,8 @@ static int wpa_driver_nl80211_try_connect( #ifdef CONFIG_DRIVER_NL80211_QCA if (params->req_key_mgmt_offload && params->psk && - (params->key_mgmt_suite == WPA_KEY_MGMT_PSK || - params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || - params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { + (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) || + wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) { wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); ret = issue_key_mgmt_set_key(drv, params->psk, 32); if (ret) @@ -6535,13 +6963,13 @@ static int wpa_driver_nl80211_try_connect( goto fail; #ifdef CONFIG_SAE - if (( -#ifdef CONFIG_DRIVER_NL80211_BRCM - (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) || +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) + if (((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) || + (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) && #else - params->key_mgmt_suite == WPA_KEY_MGMT_SAE || -#endif /* CONFIG_DRIVER_NL80211_BRCM */ - params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) && + if ((wpa_key_mgmt_sae(params->key_mgmt_suite) || + wpa_key_mgmt_sae(params->allowed_key_mgmts)) && +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ nl80211_put_sae_pwe(msg, params->sae_pwe) < 0) goto fail; #endif /* CONFIG_SAE */ @@ -6649,13 +7077,13 @@ static int wpa_driver_nl80211_associate( if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; - if ( -#ifdef CONFIG_DRIVER_NL80211_BRCM - (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) || +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) + if ((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) || + (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) #else - params->key_mgmt_suite == WPA_KEY_MGMT_SAE || -#endif /* CONFIG_DRIVER_NL80211_BRCM */ - params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) + if (wpa_key_mgmt_sae(params->key_mgmt_suite) || + wpa_key_mgmt_sae(params->allowed_key_mgmts)) +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ bss->use_nl_connect = 1; else bss->use_nl_connect = 0; @@ -6969,14 +7397,17 @@ 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); @@ -6984,7 +7415,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; @@ -7177,16 +7608,26 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, + [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, + [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 }, + [NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, - [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, - [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 }, + [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64 }, + [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8}, [NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 }, + [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_S8 }, + [NL80211_STA_INFO_RX_MPDUS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_FCS_ERROR_COUNT] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 }, - [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 }, }; struct nlattr *rate[NL80211_RATE_INFO_MAX + 1]; static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { @@ -7196,6 +7637,10 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 }, [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, [NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_HE_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_HE_NSS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_HE_GI] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_HE_DCM] = { .type = NLA_U8 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -7238,34 +7683,62 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]); data->bytes_64bit = 1; } + if (stats[NL80211_STA_INFO_SIGNAL]) + data->signal = (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]); if (stats[NL80211_STA_INFO_RX_PACKETS]) data->rx_packets = nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); if (stats[NL80211_STA_INFO_TX_PACKETS]) data->tx_packets = nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); - if (stats[NL80211_STA_INFO_RX_DURATION]) - data->rx_airtime = - nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]); - if (stats[NL80211_STA_INFO_TX_DURATION]) - data->tx_airtime = - nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]); + if (stats[NL80211_STA_INFO_TX_RETRIES]) + data->tx_retry_count = + nla_get_u32(stats[NL80211_STA_INFO_TX_RETRIES]); if (stats[NL80211_STA_INFO_TX_FAILED]) data->tx_retry_failed = nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); - if (stats[NL80211_STA_INFO_SIGNAL]) - data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]); - if (stats[NL80211_STA_INFO_ACK_SIGNAL]) { - data->last_ack_rssi = - nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]); - data->flags |= STA_DRV_DATA_LAST_ACK_RSSI; - } - + if (stats[NL80211_STA_INFO_SIGNAL_AVG]) + data->avg_signal = + (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]); if (stats[NL80211_STA_INFO_CONNECTED_TIME]) { data->connected_sec = nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]); data->flags |= STA_DRV_DATA_CONN_TIME; } + if (stats[NL80211_STA_INFO_BEACON_LOSS]) + data->beacon_loss_count = + nla_get_u32(stats[NL80211_STA_INFO_BEACON_LOSS]); + if (stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) + data->expected_throughput = + nla_get_u32(stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]); + if (stats[NL80211_STA_INFO_RX_DROP_MISC]) + data->rx_drop_misc = + nla_get_u64(stats[NL80211_STA_INFO_RX_DROP_MISC]); + if (stats[NL80211_STA_INFO_BEACON_RX]) + data->beacons_count = + nla_get_u64(stats[NL80211_STA_INFO_BEACON_RX]); + if (stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]) + data->avg_beacon_signal = + (s8) nla_get_u8(stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]); + if (stats[NL80211_STA_INFO_RX_DURATION]) + data->rx_airtime = + nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]); + if (stats[NL80211_STA_INFO_ACK_SIGNAL]) { + data->last_ack_rssi = + nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]); + data->flags |= STA_DRV_DATA_LAST_ACK_RSSI; + } + if (stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]) + data->avg_ack_signal = + nla_get_s8(stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]); + if (stats[NL80211_STA_INFO_RX_MPDUS]) + data->rx_mpdus = nla_get_u32(stats[NL80211_STA_INFO_RX_MPDUS]); + if (stats[NL80211_STA_INFO_FCS_ERROR_COUNT]) + data->fcs_error_count = + nla_get_u32(stats[NL80211_STA_INFO_FCS_ERROR_COUNT]); + if (stats[NL80211_STA_INFO_TX_DURATION]) + data->tx_airtime = + nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]); if (stats[NL80211_STA_INFO_TX_BITRATE] && nla_parse_nested(rate, NL80211_RATE_INFO_MAX, @@ -7278,6 +7751,10 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) data->current_tx_rate = nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]); + /* Convert from 100 kbps to kbps; it's a more convenient unit. + * It's also safe up until ~1Tbps. */ + data->current_tx_rate = data->current_tx_rate * 100; + if (rate[NL80211_RATE_INFO_MCS]) { data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]); data->flags |= STA_DRV_DATA_TX_MCS; @@ -7287,13 +7764,44 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]); data->flags |= STA_DRV_DATA_TX_VHT_MCS; } - if (rate[NL80211_RATE_INFO_SHORT_GI]) + if (rate[NL80211_RATE_INFO_SHORT_GI]) { + data->tx_guard_interval = GUARD_INTERVAL_0_4; data->flags |= STA_DRV_DATA_TX_SHORT_GI; + } if (rate[NL80211_RATE_INFO_VHT_NSS]) { data->tx_vht_nss = nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]); data->flags |= STA_DRV_DATA_TX_VHT_NSS; } + if (rate[NL80211_RATE_INFO_HE_MCS]) { + data->tx_hemcs = + nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]); + data->flags |= STA_DRV_DATA_TX_HE_MCS; + } + if (rate[NL80211_RATE_INFO_HE_NSS]) { + data->tx_he_nss = + nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]); + data->flags |= STA_DRV_DATA_TX_HE_NSS; + } + if (rate[NL80211_RATE_INFO_HE_GI]) { + switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) { + case NL80211_RATE_INFO_HE_GI_0_8: + data->tx_guard_interval = GUARD_INTERVAL_0_8; + break; + case NL80211_RATE_INFO_HE_GI_1_6: + data->tx_guard_interval = GUARD_INTERVAL_1_6; + break; + case NL80211_RATE_INFO_HE_GI_3_2: + data->tx_guard_interval = GUARD_INTERVAL_3_2; + break; + } + data->flags |= STA_DRV_DATA_TX_HE_GI; + } + if (rate[NL80211_RATE_INFO_HE_DCM]) { + data->tx_dcm = + nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]); + data->flags |= STA_DRV_DATA_TX_HE_DCM; + } } if (stats[NL80211_STA_INFO_RX_BITRATE] && @@ -7307,9 +7815,12 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) data->current_rx_rate = nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]); + /* Convert from 100 kbps to kbps; it's a more convenient unit. + * It's also safe up until ~1Tbps. */ + data->current_rx_rate = data->current_rx_rate * 100; + if (rate[NL80211_RATE_INFO_MCS]) { - data->rx_mcs = - nla_get_u8(rate[NL80211_RATE_INFO_MCS]); + data->rx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]); data->flags |= STA_DRV_DATA_RX_MCS; } if (rate[NL80211_RATE_INFO_VHT_MCS]) { @@ -7317,13 +7828,44 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]); data->flags |= STA_DRV_DATA_RX_VHT_MCS; } - if (rate[NL80211_RATE_INFO_SHORT_GI]) + if (rate[NL80211_RATE_INFO_SHORT_GI]) { + data->rx_guard_interval = GUARD_INTERVAL_0_4; data->flags |= STA_DRV_DATA_RX_SHORT_GI; + } if (rate[NL80211_RATE_INFO_VHT_NSS]) { data->rx_vht_nss = nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]); data->flags |= STA_DRV_DATA_RX_VHT_NSS; } + if (rate[NL80211_RATE_INFO_HE_MCS]) { + data->rx_hemcs = + nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]); + data->flags |= STA_DRV_DATA_RX_HE_MCS; + } + if (rate[NL80211_RATE_INFO_HE_NSS]) { + data->rx_he_nss = + nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]); + data->flags |= STA_DRV_DATA_RX_HE_NSS; + } + if (rate[NL80211_RATE_INFO_HE_GI]) { + switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) { + case NL80211_RATE_INFO_HE_GI_0_8: + data->rx_guard_interval = GUARD_INTERVAL_0_8; + break; + case NL80211_RATE_INFO_HE_GI_1_6: + data->rx_guard_interval = GUARD_INTERVAL_1_6; + break; + case NL80211_RATE_INFO_HE_GI_3_2: + data->rx_guard_interval = GUARD_INTERVAL_3_2; + break; + } + data->flags |= STA_DRV_DATA_RX_HE_GI; + } + if (rate[NL80211_RATE_INFO_HE_DCM]) { + data->rx_dcm = + nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]); + data->flags |= STA_DRV_DATA_RX_HE_DCM; + } } if (stats[NL80211_STA_INFO_TID_STATS]) @@ -7332,6 +7874,26 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) return NL_SKIP; } + +int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + const u8 *bssid, + struct hostap_sta_driver_data *data) +{ + struct nl_msg *msg; + + data->signal = -WPA_INVALID_NOISE; + data->current_tx_rate = 0; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL); +} + + static int i802_read_sta_data(struct i802_bss *bss, struct hostap_sta_driver_data *data, const u8 *addr) @@ -7479,7 +8041,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, struct ieee80211_mgmt mgmt; u8 channel; - if (ieee80211_freq_to_chan(bss->freq, &channel) == + if (ieee80211_freq_to_chan(bss->flink->freq, &channel) == HOSTAPD_MODE_IEEE80211AD) { /* Deauthentication is not used in DMG/IEEE 802.11ad; * disassociate the STA instead. */ @@ -8072,7 +8634,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, if (!addr && (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || type == WPA_IF_P2P_GO || type == WPA_IF_MESH || - type == WPA_IF_STATION)) { + type == WPA_IF_STATION || type == WPA_IF_AP_BSS)) { /* Enforce unique address */ u8 new_addr[ETH_ALEN]; @@ -8102,12 +8664,18 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, if (type == WPA_IF_AP_BSS && setup_ap) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); + unsigned int i; + if (new_bss == NULL) { if (added) nl80211_remove_iface(drv, ifidx); return -1; } + /* Initialize here before any failure path */ + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) + new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA; + if (bridge && i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " @@ -8131,7 +8699,11 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, new_bss->ifindex = ifidx; new_bss->drv = drv; new_bss->next = drv->first_bss->next; - new_bss->freq = drv->first_bss->freq; + new_bss->flink = &new_bss->links[0]; + new_bss->n_links = 1; + os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN); + + new_bss->flink->freq = drv->first_bss->flink->freq; new_bss->ctx = bss_ctx; new_bss->added_if = added; drv->first_bss->next = new_bss; @@ -8223,7 +8795,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); nl80211_teardown_ap(bss); if (!bss->added_if && !drv->first_bss->next) - wpa_driver_nl80211_del_beacon(bss); + wpa_driver_nl80211_del_beacon_all(bss); nl80211_destroy_bss(bss); if (!bss->added_if) i802_set_iface_flags(bss, 0); @@ -8298,6 +8870,11 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, if (save_cookie) drv->send_frame_cookie = no_ack ? (u64) -1 : cookie; + if (!wait) { + /* There is no need to store this cookie since there + * is no wait that could be canceled later. */ + goto fail; + } if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) { wpa_printf(MSG_DEBUG, "nl80211: Drop oldest pending send frame cookie 0x%llx", @@ -8333,8 +8910,8 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, struct ieee80211_hdr *hdr; int offchanok = 1; - if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq && - bss->beacon_set) + if (is_ap_interface(drv->nlmode) && (int) freq == bss->flink->freq && + bss->flink->beacon_set) offchanok = 0; wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " @@ -8370,7 +8947,7 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, modes = nl80211_get_hw_feature_data(bss, &num_modes, &flags, &dfs_domain); if (dfs_domain != HOSTAPD_DFS_REGION_ETSI && - ieee80211_is_dfs(bss->freq, modes, num_modes)) + ieee80211_is_dfs(bss->flink->freq, modes, num_modes)) offchanok = 0; if (modes) { for (i = 0; i < num_modes; i++) { @@ -8384,7 +8961,7 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, if (is_ap_interface(drv->nlmode) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || - (int) freq == bss->freq || drv->device_ap_sme || + (int) freq == bss->flink->freq || drv->device_ap_sme || !drv->use_monitor)) ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, 0, freq, no_cck, offchanok, @@ -8428,7 +9005,8 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) u64 cookie; /* Cancel the last pending TX cookie */ - nl80211_frame_wait_cancel(bss, drv->send_frame_cookie); + if (drv->send_frame_cookie != (u64) -1) + nl80211_frame_wait_cancel(bss, drv->send_frame_cookie); /* * Cancel the other pending TX cookies, if any. This is needed since @@ -8613,14 +9191,71 @@ fail: } +static void nl80211_remove_links(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + u8 link_id; + + while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) { + struct i802_link *link = &bss->links[0]; + + wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u", + link->link_id); + + wpa_driver_nl80211_del_beacon(bss, link); + + link_id = link->link_id; + + /* First remove the link locally */ + if (bss->n_links == 1) { + bss->flink->link_id = NL80211_DRV_LINK_ID_NA; + os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN); + } else { + struct i802_link *other = &bss->links[bss->n_links - 1]; + + os_memcpy(link, other, sizeof(*link)); + other->link_id = NL80211_DRV_LINK_ID_NA; + os_memset(other->addr, 0, ETH_ALEN); + + bss->n_links--; + } + + /* Remove the link from the kernel */ + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) { + nlmsg_free(msg); + wpa_printf(MSG_ERROR, + "nl80211: remove link (%d) failed", + link_id); + return; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: remove link (%d) failed. ret=%d (%s)", + link_id, ret, strerror(-ret)); + return; + } + } +} + + static int wpa_driver_nl80211_deinit_ap(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) return -1; - wpa_driver_nl80211_del_beacon(bss); - bss->beacon_set = 0; + + /* Stop beaconing */ + wpa_driver_nl80211_del_beacon(bss, bss->flink); + + nl80211_remove_links(bss); /* * If the P2P GO interface was dynamically added, then it is @@ -8637,10 +9272,12 @@ static int wpa_driver_nl80211_stop_ap(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) return -1; - wpa_driver_nl80211_del_beacon(bss); - bss->beacon_set = 0; + + wpa_driver_nl80211_del_beacon_all(bss); + return 0; } @@ -8744,12 +9381,12 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) int res; os_memset(si, 0, sizeof(*si)); - res = nl80211_get_link_signal(drv, si); + res = nl80211_get_link_signal(drv, drv->bssid, &si->data); if (res) { if (drv->nlmode != NL80211_IFTYPE_ADHOC && drv->nlmode != NL80211_IFTYPE_MESH_POINT) return res; - si->current_signal = 0; + si->data.signal = 0; } res = nl80211_get_channel_width(drv, si); @@ -8760,6 +9397,163 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) } +static int get_links_noise(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_mlo_signal_info *mlo_sig = arg; + int i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to parse nested attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(mlo_sig->valid_links & BIT(i))) + continue; + + if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + mlo_sig->links[i].frequency) + continue; + + mlo_sig->links[i].current_noise = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + break; + } + + return NL_SKIP; +} + + +static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_mlo_signal_info *mlo_sig) +{ + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig, + NULL, NULL); +} + + +static int get_links_channel_width(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_mlo_signal_info *mlo_sig = arg; + struct nlattr *link; + int rem_links; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_MLO_LINKS]) + return NL_SKIP; + + nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem_links) { + struct nlattr *tb2[NL80211_ATTR_MAX + 1]; + int link_id; + + nla_parse(tb2, NL80211_ATTR_MAX, nla_data(link), nla_len(link), + NULL); + + if (!tb2[NL80211_ATTR_MLO_LINK_ID]) + continue; + + link_id = nla_get_u8(tb2[NL80211_ATTR_MLO_LINK_ID]); + if (link_id >= MAX_NUM_MLD_LINKS) + continue; + + if (!tb2[NL80211_ATTR_CHANNEL_WIDTH]) + continue; + mlo_sig->links[link_id].chanwidth = convert2width( + nla_get_u32(tb2[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb2[NL80211_ATTR_CENTER_FREQ1]) + mlo_sig->links[link_id].center_frq1 = + nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ1]); + if (tb2[NL80211_ATTR_CENTER_FREQ2]) + mlo_sig->links[link_id].center_frq2 = + nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ2]); + } + + return NL_SKIP; +} + + +static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv, + struct wpa_mlo_signal_info *mlo_sig) +{ + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig, + NULL, NULL); +} + + +static int nl80211_mlo_signal_poll(void *priv, + struct wpa_mlo_signal_info *mlo_si) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + int i; + + if (drv->nlmode != NL80211_IFTYPE_STATION || + !drv->sta_mlo_info.valid_links) + return -1; + + os_memset(mlo_si, 0, sizeof(*mlo_si)); + mlo_si->valid_links = drv->sta_mlo_info.valid_links; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(mlo_si->valid_links & BIT(i))) + continue; + + res = nl80211_get_link_signal(drv, + drv->sta_mlo_info.links[i].bssid, + &mlo_si->links[i].data); + if (res != 0) + return res; + + mlo_si->links[i].center_frq1 = -1; + mlo_si->links[i].center_frq2 = -1; + mlo_si->links[i].chanwidth = CHAN_WIDTH_UNKNOWN; + mlo_si->links[i].current_noise = WPA_INVALID_NOISE; + mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq; + } + + res = nl80211_get_links_channel_width(drv, mlo_si); + if (res != 0) + return res; + + return nl80211_get_links_noise(drv, mlo_si); +} + + static int nl80211_set_param(void *priv, const char *param) { struct i802_bss *bss = priv; @@ -8909,10 +9703,14 @@ static const char * nl80211_get_radio_name(void *priv) static int nl80211_pmkid(struct i802_bss *bss, int cmd, - struct wpa_pmkid_params *params) + struct wpa_pmkid_params *params, bool skip_pmk) { struct nl_msg *msg; - const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */ + + if (cmd == NL80211_CMD_SET_PMKSA) + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_SET_PMKSA with skip_pmk=%s pmk_len=%zu", + skip_pmk ? "true" : "false", params->pmk_len); if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || (params->pmkid && @@ -8931,7 +9729,7 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD, params->pmk_reauth_threshold)) || (cmd != NL80211_CMD_DEL_PMKSA && - params->pmk_len && params->pmk_len <= PMK_MAX_LEN && + params->pmk_len && !skip_pmk && nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) { nl80211_nlmsg_clear(msg); nlmsg_free(msg); @@ -8945,6 +9743,9 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params) { struct i802_bss *bss = priv; + const size_t PMK_MAX_LEN = 64; /* current cfg80211 limit */ + const size_t LEGACY_PMK_MAX_LEN = 48; /* old cfg80211 limit */ + bool skip_pmk = params->pmk_len > PMK_MAX_LEN; int ret; if (params->bssid) @@ -8957,7 +9758,15 @@ static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params) wpa_ssid_txt(params->ssid, params->ssid_len)); } - ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params); + ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, skip_pmk); + /* + * Try again by skipping PMK if the first attempt failed with ERANGE + * error, PMK was not skipped, and PMK length is greater than the + * legacy kernel maximum allowed limit. + */ + if (ret == -ERANGE && !skip_pmk && + params->pmk_len > LEGACY_PMK_MAX_LEN) + ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, true); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)", @@ -8983,7 +9792,7 @@ static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params) wpa_ssid_txt(params->ssid, params->ssid_len)); } - ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params); + ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params, true); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)", @@ -9770,8 +10579,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) bss->ifname, bss->brname, MAC2STR(bss->addr), - bss->freq, - bss->beacon_set ? "beacon_set=1\n" : "", + bss->flink->freq, + bss->flink->beacon_set ? "beacon_set=1\n" : "", bss->added_if_into_bridge ? "added_if_into_bridge=1\n" : "", bss->already_in_bridge ? "already_in_bridge=1\n" : "", @@ -9848,6 +10657,34 @@ 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" + "link_freq[%u]=%u\n", + i, MAC2STR(mlo->links[i].addr), + i, MAC2STR(mlo->links[i].bssid), + i, mlo->links[i].freq); + 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" @@ -9871,7 +10708,9 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.max_conc_chan_5_0=%u\n" "capa.max_sched_scan_plans=%u\n" "capa.max_sched_scan_plan_interval=%u\n" - "capa.max_sched_scan_plan_iterations=%u\n", + "capa.max_sched_scan_plan_iterations=%u\n" + "capa.mbssid_max_interfaces=%u\n" + "capa.ema_max_periodicity=%u\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, @@ -9893,7 +10732,9 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.max_conc_chan_5_0, drv->capa.max_sched_scan_plans, drv->capa.max_sched_scan_plan_interval, - drv->capa.max_sched_scan_plan_iterations); + drv->capa.max_sched_scan_plan_iterations, + drv->capa.mbssid_max_interfaces, + drv->capa.ema_max_periodicity); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -9957,7 +10798,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) int i; wpa_printf(MSG_DEBUG, - "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d%s%s%s)", + "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x%s%s%s)", settings->cs_count, settings->block_tx, settings->freq_params.freq, settings->freq_params.channel, @@ -9965,6 +10806,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) settings->freq_params.bandwidth, settings->freq_params.center_freq1, settings->freq_params.center_freq2, + settings->punct_bitmap, settings->freq_params.ht_enabled ? " ht" : "", settings->freq_params.vht_enabled ? " vht" : "", settings->freq_params.he_enabled ? " he" : ""); @@ -10035,7 +10877,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) settings->cs_count) || (ret = nl80211_put_freq_params(msg, &settings->freq_params)) || (settings->block_tx && - nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX))) + nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) || + (settings->punct_bitmap && + nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, + settings->punct_bitmap))) goto error; /* beacon_after params */ @@ -10078,6 +10923,87 @@ error: } +#ifdef CONFIG_IEEE80211AX +static int nl80211_switch_color(void *priv, struct cca_settings *settings) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *beacon_cca; + struct nl_msg *msg; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, + "nl80211: Color change request (cca_count=%u color=%d)", + settings->cca_count, settings->cca_color); + + if (drv->nlmode != NL80211_IFTYPE_AP) + return -EOPNOTSUPP; + + if (!settings->beacon_cca.tail) + return -EINVAL; + + if (settings->beacon_cca.tail_len <= settings->counter_offset_beacon || + settings->beacon_cca.tail[settings->counter_offset_beacon] != + settings->cca_count) + return -EINVAL; + + if (settings->beacon_cca.probe_resp && + (settings->beacon_cca.probe_resp_len <= + settings->counter_offset_presp || + settings->beacon_cca.probe_resp[settings->counter_offset_presp] != + settings->cca_count)) + return -EINVAL; + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_COLOR_CHANGE_REQUEST); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COUNT, + settings->cca_count) || + nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COLOR, + settings->cca_color)) + goto error; + + /* beacon_after params */ + ret = set_beacon_data(msg, &settings->beacon_after); + if (ret) + goto error; + + /* beacon_csa params */ + beacon_cca = nla_nest_start(msg, NL80211_ATTR_COLOR_CHANGE_ELEMS); + if (!beacon_cca) { + ret = -ENOBUFS; + goto error; + } + + ret = set_beacon_data(msg, &settings->beacon_cca); + if (ret) + goto error; + + if (nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_BEACON, + settings->counter_offset_beacon) || + (settings->beacon_cca.probe_resp && + nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_PRESP, + settings->counter_offset_presp))) { + ret = -ENOBUFS; + goto error; + } + + nla_nest_end(msg, beacon_cca); + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: switch_color failed err=%d (%s)", + ret, strerror(-ret)); + } + return ret; + +error: + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, "nl80211: Could not build color switch request"); + return ret; +} +#endif /* CONFIG_IEEE80211AX */ + + static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr, u8 user_priority, u16 admitted_time) { @@ -10547,17 +11473,17 @@ static int nl80211_set_mac_addr(void *priv, const u8 *addr) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int new_addr = addr != NULL; -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) struct nl_msg *msg; struct nlattr *params; int ret; -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ wpa_printf(MSG_DEBUG, "Enter: %s", __FUNCTION__); if (TEST_FAIL()) return -1; if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) { -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) if (!addr ) { addr = drv->global->p2p_perm_addr; } @@ -10584,7 +11510,7 @@ static int nl80211_set_mac_addr(void *priv, const u8 *addr) return ret; #else return -ENOTSUP; -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ } if (!addr) addr = drv->perm_addr; @@ -10608,6 +11534,7 @@ static int nl80211_set_mac_addr(void *priv, const u8 *addr) wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR, bss->ifname, MAC2STR(addr)); drv->addr_changed = new_addr; + os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN); os_memcpy(bss->addr, addr, ETH_ALEN); if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0) @@ -10752,7 +11679,7 @@ static int nl80211_join_mesh(struct i802_bss *bss, goto fail; } ret = 0; - drv->assoc_freq = bss->freq = params->freq.freq; + drv->assoc_freq = bss->flink->freq = params->freq.freq; wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); fail: @@ -10808,7 +11735,7 @@ static int wpa_driver_nl80211_leave_mesh(void *priv) } else { wpa_printf(MSG_DEBUG, "nl80211: mesh leave request send successfully"); - drv->first_bss->freq = 0; + drv->first_bss->flink->freq = 0; } if (drv->start_mode_sta && @@ -11049,6 +11976,8 @@ static const char * drv_br_port_attr_str(enum drv_br_port_attr attr) return "proxyarp_wifi"; case DRV_BR_PORT_ATTR_HAIRPIN_MODE: return "hairpin_mode"; + case DRV_BR_PORT_ATTR_MCAST2UCAST: + return "multicast_to_unicast"; } return NULL; @@ -11233,6 +12162,8 @@ static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv, nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) || (params->vht_enabled && nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) || + (params->eht_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED)) || nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) || add_acs_ch_list(msg, params->freq_list) || @@ -11245,9 +12176,10 @@ static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv, nla_nest_end(msg, data); wpa_printf(MSG_DEBUG, - "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d EDMG: %d", + "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d EHT: %d BW: %d EDMG: %d", params->hw_mode, params->ht_enabled, params->ht40_enabled, - params->vht_enabled, params->ch_width, params->edmg_enabled); + params->vht_enabled, params->eht_enabled, params->ch_width, + params->edmg_enabled); ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret) { @@ -11319,9 +12251,33 @@ static int nl80211_set_band(void *priv, u32 band_mask) struct nl80211_pcl { unsigned int num; - unsigned int *freq_list; + struct weighted_pcl *freq_list; }; +static void get_pcl_attr_values(struct weighted_pcl *wpcl, struct nlattr *nl[]) +{ + if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ]) + wpcl->freq = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ]); + if (nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT]) + wpcl->weight = nla_get_u8(nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT]); + if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]) { + u32 flags = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]); + + wpcl->flag = 0; + if (flags & BIT(0)) + wpcl->flag |= WEIGHTED_PCL_GO; + if (flags & BIT(1)) + wpcl->flag |= WEIGHTED_PCL_CLI; + if (flags & BIT(2)) + wpcl->flag |= WEIGHTED_PCL_MUST_CONSIDER; + if (flags & BIT(3)) + wpcl->flag |= WEIGHTED_PCL_EXCLUDE; + } else { + wpcl->flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI; + } +} + + static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -11330,6 +12286,7 @@ static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) struct nlattr *nl_vend, *attr; enum qca_iface_type iface_type; struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + struct nlattr *nl_pcl[QCA_WLAN_VENDOR_ATTR_PCL_MAX + 1]; unsigned int num, max_num; u32 *freqs; @@ -11355,26 +12312,69 @@ static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d", iface_type); - attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; - if (!attr) { + attr = tb_vendor[ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL]; + if (attr) { + int rem; + struct nlattr *wpcl = attr; + unsigned int i; + + num = 0; + nla_for_each_nested(attr, wpcl, rem) { + if (num == param->num) + break; /* not enough room for all entries */ + if (nla_parse(nl_pcl, QCA_WLAN_VENDOR_ATTR_PCL_MAX, + nla_data(attr), nla_len(attr), NULL)) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to parse PCL info"); + param->num = 0; + return NL_SKIP; + } + get_pcl_attr_values(¶m->freq_list[num], nl_pcl); + num++; + } + param->num = num; + + /* Sort frequencies based on their weight */ + for (i = 0; i < num; i++) { + unsigned int j; + + for (j = i + 1; j < num; j++) { + if (param->freq_list[i].weight < + param->freq_list[j].weight) { + struct weighted_pcl tmp; + + tmp = param->freq_list[i]; + param->freq_list[i] = + param->freq_list[j]; + param->freq_list[j] = tmp; + } + } + } + } else if (tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]) { + wpa_printf(MSG_DEBUG, + "nl80211: Driver does not provide weighted PCL; use the non-weighted variant"); + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; + /* + * param->num has the maximum number of entries for which there + * is room in the freq_list provided by the caller. + */ + freqs = nla_data(attr); + max_num = nla_len(attr) / sizeof(u32); + if (max_num > param->num) + max_num = param->num; + for (num = 0; num < max_num; num++) { + param->freq_list[num].freq = freqs[num]; + param->freq_list[num].flag = + WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI; + } + param->num = num; + } else { wpa_printf(MSG_ERROR, "nl80211: preferred_freq_list couldn't be found"); param->num = 0; return NL_SKIP; } - - /* - * param->num has the maximum number of entries for which there - * is room in the freq_list provided by the caller. - */ - freqs = nla_data(attr); - max_num = nla_len(attr) / sizeof(u32); - if (max_num > param->num) - max_num = param->num; - for (num = 0; num < max_num; num++) - param->freq_list[num] = freqs[num]; - param->num = num; - return NL_SKIP; } @@ -11382,7 +12382,7 @@ static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) static int nl80211_get_pref_freq_list(void *priv, enum wpa_driver_if_type if_type, unsigned int *num, - unsigned int *freq_list) + struct weighted_pcl *freq_list) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -11439,7 +12439,8 @@ static int nl80211_get_pref_freq_list(void *priv, } nla_nest_end(msg, params); - os_memset(freq_list, 0, *num * sizeof(freq_list[0])); + if (freq_list) + os_memset(freq_list, 0, *num * sizeof(struct weighted_pcl)); ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m, NULL, NULL); if (ret) { @@ -11451,8 +12452,10 @@ static int nl80211_get_pref_freq_list(void *priv, *num = param.num; for (i = 0; i < *num; i++) { - wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d", - i, freq_list[i]); + wpa_printf(MSG_DEBUG, + "nl80211: preferred_channel_list[%d]=%d[%d]:0x%x", + i, freq_list[i].freq, freq_list[i].weight, + freq_list[i].flag); } return 0; @@ -11885,24 +12888,190 @@ fail: #endif /* CONFIG_MBO */ + +#ifdef CONFIG_PASN + +static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params) +{ + unsigned int i; + struct i802_bss *bss = priv; + struct nl_msg *msg = NULL; + struct nlattr *nlpeers, *attr, *attr1; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: PASN authentication response for %d entries", + params->num_peers); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_PASN)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + nlpeers = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEERS); + if (!nlpeers) + goto fail; + + for (i = 0; i < params->num_peers; i++) { + attr1 = nla_nest_start(msg, i); + if (!attr1 || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR, + ETH_ALEN, params->peer[i].own_addr) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR, + ETH_ALEN, params->peer[i].peer_addr)) + goto fail; + + if (params->peer[i].status == 0 && + nla_put_flag(msg, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS)) + goto fail; + + wpa_printf(MSG_DEBUG, + "nl80211: Own address[%u]: " MACSTR + " Peer address[%u]: " MACSTR " Status: %s", + i, MAC2STR(params->peer[i].own_addr), i, + MAC2STR(params->peer[i].peer_addr), + params->peer[i].status ? "Fail" : "Success"); + nla_nest_end(msg, attr1); + } + + nla_nest_end(msg, nlpeers); + nla_nest_end(msg, attr); + + return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); + +fail: + nlmsg_free(msg); + return -1; +} + + +static u32 wpa_ltf_keyseed_len_to_sha_type(size_t len) +{ + if (len == SHA384_MAC_LEN) + return QCA_WLAN_VENDOR_SHA_384; + if (len == SHA256_MAC_LEN) + return QCA_WLAN_VENDOR_SHA_256; + + wpa_printf(MSG_ERROR, "nl80211: Unexpected LTF keyseed len %zu", len); + return (u32) -1; +} + + +static int nl80211_set_secure_ranging_ctx(void *priv, + struct secure_ranging_params *params) +{ + int ret; + u32 suite; + struct nlattr *attr; + struct nl_msg *msg = NULL; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + /* Configure secure ranging context only to the drivers that support it. + */ + if (!drv->secure_ranging_ctx_vendor_cmd_avail) + return 0; + + if (!params->peer_addr || !params->own_addr) + return -1; + + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: Secure ranging context for " MACSTR, + MAC2STR(params->peer_addr)); + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR, + ETH_ALEN, params->peer_addr) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR, + ETH_ALEN, params->own_addr) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION, + params->action)) + goto fail; + + if (params->cipher) { + suite = wpa_cipher_to_cipher_suite(params->cipher); + if (!suite || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER, + suite)) + goto fail; + } + + if (params->tk_len && params->tk) { + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK, + params->tk_len, params->tk)) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "nl80211: TK", + params->tk, params->tk_len); + } + + if (params->ltf_keyseed_len && params->ltf_keyseed) { + u32 sha_type = wpa_ltf_keyseed_len_to_sha_type( + params->ltf_keyseed_len); + + if (sha_type == (u32) -1 || + nla_put_u32( + msg, + QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE, + sha_type) || + nla_put(msg, + QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED, + params->ltf_keyseed_len, params->ltf_keyseed)) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "nl80211: LTF keyseed", + params->ltf_keyseed, params->ltf_keyseed_len); + } + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, + "nl80211: Set secure ranging context failed: ret=%d (%s)", + ret, strerror(-ret)); + return ret; +fail: + nlmsg_free(msg); + return -1; +} + +#endif /* CONFIG_PASN */ + #endif /* CONFIG_DRIVER_NL80211_QCA */ static int nl80211_do_acs(void *priv, struct drv_acs_params *params) { -#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM) +#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM) \ + || defined(CONFIG_DRIVER_NL80211_SYNA) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; -#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM \ + || defined(CONFIG_DRIVER_NL80211_SYNA) */ #ifdef CONFIG_DRIVER_NL80211_QCA if (drv->qca_do_acs) return nl80211_qca_do_acs(drv, params); #endif /* CONFIG_DRIVER_NL80211_QCA */ -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) if (drv->brcm_do_acs) return wpa_driver_do_broadcom_acs(drv, params); -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ return -1; } @@ -12070,7 +13239,7 @@ static int nl80211_update_connection_params( return 0; /* Handle any connection param update here which might receive kernel handling in future */ -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) if (mask & WPA_DRV_UPDATE_TD_POLICY) { ret = nl80211_set_td_policy(priv, params->td_policy); if (ret) { @@ -12080,7 +13249,7 @@ static int nl80211_update_connection_params( } return ret; } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS); if (!msg) @@ -12235,7 +13404,7 @@ static int nl80211_dpp_listen(void *priv, bool enable) } #endif /* CONFIG_DPP */ -#ifdef CONFIG_DRIVER_NL80211_BRCM +#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) static int nl80211_set_td_policy(void *priv, u32 td_policy) { struct i802_bss *bss = priv; @@ -12264,7 +13433,78 @@ static int nl80211_set_td_policy(void *priv, u32 td_policy) return ret; } -#endif /* CONFIG_DRIVER_NL80211_BRCM */ +#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */ + +static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + unsigned int idx, i; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR, + link_id, MAC2STR(addr)); + + if (drv->nlmode != NL80211_IFTYPE_AP) { + wpa_printf(MSG_DEBUG, + "nl80211: MLD: cannot add link to iftype=%u", + drv->nlmode); + return -EINVAL; + } + + if (bss->n_links >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu", + bss->n_links); + return -EINVAL; + } + + for (i = 0; i < bss->n_links; i++) { + if (bss->links[i].link_id == link_id && + bss->links[i].beacon_set) { + wpa_printf(MSG_DEBUG, + "nl80211: MLD: link already set"); + return -EINVAL; + } + } + + /* try using the first link entry, assuming it is not beaconing yet */ + if (bss->n_links == 1 && + bss->flink->link_id == NL80211_DRV_LINK_ID_NA) { + if (bss->flink->beacon_set) { + wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing"); + return -EINVAL; + } + + idx = 0; + } else { + idx = bss->n_links; + } + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: add link failed. ret=%d (%s)", + ret, strerror(-ret)); + return ret; + } + + bss->links[idx].link_id = link_id; + os_memcpy(bss->links[idx].addr, addr, ETH_ALEN); + + bss->n_links = idx + 1; + + wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links); + return 0; +} + #ifdef CONFIG_TESTING_OPTIONS @@ -12372,6 +13612,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .resume = wpa_driver_nl80211_resume, .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, + .mlo_signal_poll = nl80211_mlo_signal_poll, .channel_info = nl80211_channel_info, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, @@ -12395,6 +13636,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_survey = wpa_driver_nl80211_get_survey, .status = wpa_driver_nl80211_status, .switch_channel = nl80211_switch_channel, +#ifdef CONFIG_IEEE80211AX + .switch_color = nl80211_switch_color, +#endif /* CONFIG_IEEE80211AX */ #ifdef ANDROID_P2P .set_noa = wpa_driver_set_p2p_noa, .get_noa = wpa_driver_get_p2p_noa, @@ -12439,6 +13683,10 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { #endif /* CONFIG_MBO */ .set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow, .add_sta_node = nl80211_add_sta_node, +#ifdef CONFIG_PASN + .send_pasn_resp = nl80211_send_pasn_resp, + .set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx, +#endif /* CONFIG_PASN */ #endif /* CONFIG_DRIVER_NL80211_QCA */ .do_acs = nl80211_do_acs, .configure_data_frame_filters = nl80211_configure_data_frame_filters, @@ -12449,6 +13697,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { #ifdef CONFIG_DPP .dpp_listen = nl80211_dpp_listen, #endif /* CONFIG_DPP */ + .get_sta_mlo_info = nl80211_get_sta_mlo_info, + .link_add = nl80211_link_add, #ifdef CONFIG_TESTING_OPTIONS .register_frame = testing_nl80211_register_frame, .radio_disable = testing_nl80211_radio_disable, |