aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/driver_nl80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/driver_nl80211.c')
-rw-r--r--src/drivers/driver_nl80211.c1810
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 = &params->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(&param->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, &param,
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,