summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2022-08-26 11:56:55 +0100
committerDavid S. Miller <davem@davemloft.net>2022-08-26 11:56:55 +0100
commit643952f3ecace9e20b8a0c5cd1bbd7409ac2d02c (patch)
treeee6cf29278a3e461a074cd36d200f581bc864e15 /net
parent76d7df9406a1d2faec6eaaa1d835a1dbc1d49cec (diff)
parent4ffb4d25ef1251d57881da183d6bec7f2dfe1e32 (diff)
downloadlinux-643952f3ecace9e20b8a0c5cd1bbd7409ac2d02c.tar.bz2
Merge tag 'wireless-next-2022-08-26-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next
Johannes berg says: ==================== Various updates: * rtw88: operation, locking, warning, and code style fixes * rtw89: small updates * cfg80211/mac80211: more EHT/MLO (802.11be, WiFi 7) work * brcmfmac: a couple of fixes * misc cleanups etc. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c129
-rw-r--r--net/mac80211/eht.c4
-rw-r--r--net/mac80211/ethtool.c10
-rw-r--r--net/mac80211/ibss.c8
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/iface.c13
-rw-r--r--net/mac80211/key.c190
-rw-r--r--net/mac80211/key.h13
-rw-r--r--net/mac80211/mesh.c2
-rw-r--r--net/mac80211/mlme.c73
-rw-r--r--net/mac80211/rx.c200
-rw-r--r--net/mac80211/sta_info.c9
-rw-r--r--net/mac80211/tx.c65
-rw-r--r--net/mac80211/util.c32
-rw-r--r--net/wireless/core.c16
-rw-r--r--net/wireless/ibss.c2
-rw-r--r--net/wireless/nl80211.c186
-rw-r--r--net/wireless/rdev-ops.h58
-rw-r--r--net/wireless/reg.c4
-rw-r--r--net/wireless/scan.c2
-rw-r--r--net/wireless/sme.c5
-rw-r--r--net/wireless/trace.h97
-rw-r--r--net/wireless/util.c4
-rw-r--r--net/wireless/wext-compat.c18
24 files changed, 859 insertions, 289 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index a4f6971b7a19..b5522edbe05d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -23,6 +23,30 @@
#include "mesh.h"
#include "wme.h"
+static struct ieee80211_link_data *
+ieee80211_link_or_deflink(struct ieee80211_sub_if_data *sdata, int link_id,
+ bool require_valid)
+{
+ struct ieee80211_link_data *link;
+
+ if (link_id < 0) {
+ /*
+ * For keys, if sdata is not an MLD, we might not use
+ * the return value at all (if it's not a pairwise key),
+ * so in that case (require_valid==false) don't error.
+ */
+ if (require_valid && sdata->vif.valid_links)
+ return ERR_PTR(-EINVAL);
+
+ return &sdata->deflink;
+ }
+
+ link = sdata_dereference(sdata->link[link_id], sdata);
+ if (!link)
+ return ERR_PTR(-ENOLINK);
+ return link;
+}
+
static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
struct vif_params *params)
{
@@ -434,10 +458,12 @@ static int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata,
}
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, bool pairwise, const u8 *mac_addr,
- struct key_params *params)
+ int link_id, u8 key_idx, bool pairwise,
+ const u8 *mac_addr, struct key_params *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_link_data *link =
+ ieee80211_link_or_deflink(sdata, link_id, false);
struct ieee80211_local *local = sdata->local;
struct sta_info *sta = NULL;
struct ieee80211_key *key;
@@ -446,6 +472,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (!ieee80211_sdata_running(sdata))
return -ENETDOWN;
+ if (IS_ERR(link))
+ return PTR_ERR(link);
+
if (pairwise && params->mode == NL80211_KEY_SET_TX)
return ieee80211_set_tx(sdata, mac_addr, key_idx);
@@ -454,6 +483,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_WEP104:
+ if (link_id >= 0)
+ return -EINVAL;
if (WARN_ON_ONCE(fips_enabled))
return -EINVAL;
break;
@@ -466,6 +497,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (IS_ERR(key))
return PTR_ERR(key);
+ key->conf.link_id = link_id;
+
if (pairwise)
key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
@@ -527,7 +560,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
break;
}
- err = ieee80211_key_link(key, sdata, sta);
+ err = ieee80211_key_link(key, link, sta);
out_unlock:
mutex_unlock(&local->sta_mtx);
@@ -536,18 +569,37 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
}
static struct ieee80211_key *
-ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata,
+ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
u8 key_idx, bool pairwise, const u8 *mac_addr)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_link_data *link = &sdata->deflink;
struct ieee80211_key *key;
- struct sta_info *sta;
+
+ if (link_id >= 0) {
+ link = rcu_dereference_check(sdata->link[link_id],
+ lockdep_is_held(&sdata->wdev.mtx));
+ if (!link)
+ return NULL;
+ }
if (mac_addr) {
+ struct sta_info *sta;
+ struct link_sta_info *link_sta;
+
sta = sta_info_get_bss(sdata, mac_addr);
if (!sta)
return NULL;
+ if (link_id >= 0) {
+ link_sta = rcu_dereference_check(sta->link[link_id],
+ lockdep_is_held(&local->sta_mtx));
+ if (!link_sta)
+ return NULL;
+ } else {
+ link_sta = &sta->deflink;
+ }
+
if (pairwise && key_idx < NUM_DEFAULT_KEYS)
return rcu_dereference_check_key_mtx(local,
sta->ptk[key_idx]);
@@ -557,7 +609,7 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata,
NUM_DEFAULT_MGMT_KEYS +
NUM_DEFAULT_BEACON_KEYS)
return rcu_dereference_check_key_mtx(local,
- sta->deflink.gtk[key_idx]);
+ link_sta->gtk[key_idx]);
return NULL;
}
@@ -566,7 +618,7 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata,
return rcu_dereference_check_key_mtx(local,
sdata->keys[key_idx]);
- key = rcu_dereference_check_key_mtx(local, sdata->deflink.gtk[key_idx]);
+ key = rcu_dereference_check_key_mtx(local, link->gtk[key_idx]);
if (key)
return key;
@@ -578,7 +630,8 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata,
}
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, bool pairwise, const u8 *mac_addr)
+ int link_id, u8 key_idx, bool pairwise,
+ const u8 *mac_addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
@@ -588,7 +641,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
mutex_lock(&local->sta_mtx);
mutex_lock(&local->key_mtx);
- key = ieee80211_lookup_key(sdata, key_idx, pairwise, mac_addr);
+ key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
if (!key) {
ret = -ENOENT;
goto out_unlock;
@@ -605,8 +658,8 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, bool pairwise, const u8 *mac_addr,
- void *cookie,
+ int link_id, u8 key_idx, bool pairwise,
+ const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie,
struct key_params *params))
{
@@ -624,7 +677,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
rcu_read_lock();
- key = ieee80211_lookup_key(sdata, key_idx, pairwise, mac_addr);
+ key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
if (!key)
goto out;
@@ -711,34 +764,49 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_config_default_key(struct wiphy *wiphy,
struct net_device *dev,
- u8 key_idx, bool uni,
+ int link_id, u8 key_idx, bool uni,
bool multi)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_link_data *link =
+ ieee80211_link_or_deflink(sdata, link_id, false);
- ieee80211_set_default_key(sdata, key_idx, uni, multi);
+ if (IS_ERR(link))
+ return PTR_ERR(link);
+
+ ieee80211_set_default_key(link, key_idx, uni, multi);
return 0;
}
static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
struct net_device *dev,
- u8 key_idx)
+ int link_id, u8 key_idx)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_link_data *link =
+ ieee80211_link_or_deflink(sdata, link_id, true);
+
+ if (IS_ERR(link))
+ return PTR_ERR(link);
- ieee80211_set_default_mgmt_key(sdata, key_idx);
+ ieee80211_set_default_mgmt_key(link, key_idx);
return 0;
}
static int ieee80211_config_default_beacon_key(struct wiphy *wiphy,
struct net_device *dev,
- u8 key_idx)
+ int link_id, u8 key_idx)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_link_data *link =
+ ieee80211_link_or_deflink(sdata, link_id, true);
+
+ if (IS_ERR(link))
+ return PTR_ERR(link);
- ieee80211_set_default_beacon_key(sdata, key_idx);
+ ieee80211_set_default_beacon_key(link, key_idx);
return 0;
}
@@ -1610,6 +1678,18 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
rcu_dereference_protected(sta->link[link_id],
lockdep_is_held(&local->sta_mtx));
+ /*
+ * If there are no changes, then accept a link that doesn't exist,
+ * unless it's a new link.
+ */
+ if (params->link_id < 0 && !new_link &&
+ !params->link_mac && !params->txpwr_set &&
+ !params->supported_rates_len &&
+ !params->ht_capa && !params->vht_capa &&
+ !params->he_capa && !params->eht_capa &&
+ !params->opmode_notif_used)
+ return 0;
+
if (!link || !link_sta)
return -EINVAL;
@@ -1625,6 +1705,8 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
params->link_mac)) {
return -EINVAL;
}
+ } else if (new_link) {
+ return -EINVAL;
}
if (params->txpwr_set) {
@@ -2554,7 +2636,8 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_link_data *link = &sdata->deflink;
+ struct ieee80211_link_data *link =
+ ieee80211_link_or_deflink(sdata, params->link_id, true);
struct ieee80211_tx_queue_params p;
if (!local->ops->conf_tx)
@@ -2563,6 +2646,9 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
if (local->hw.queues < IEEE80211_NUM_ACS)
return -EOPNOTSUPP;
+ if (IS_ERR(link))
+ return PTR_ERR(link);
+
memset(&p, 0, sizeof(p));
p.aifs = params->aifs;
p.cw_max = params->cwmax;
@@ -3597,9 +3683,6 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_MESH_POINT: {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
- return -EINVAL;
-
/* changes into another band are not supported */
if (sdata->vif.bss_conf.chandef.chan->band !=
params->chandef.chan->band)
@@ -3732,7 +3815,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
IEEE80211_QUEUE_STOP_REASON_CSA);
cfg80211_ch_switch_started_notify(sdata->dev,
- &sdata->deflink.csa_chandef,
+ &sdata->deflink.csa_chandef, 0,
params->count, params->block_tx);
if (changed) {
diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c
index 31e20a342f21..18bc6b78b267 100644
--- a/net/mac80211/eht.c
+++ b/net/mac80211/eht.c
@@ -30,7 +30,9 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
return;
mcs_nss_size = ieee80211_eht_mcs_nss_size(he_cap_ie_elem,
- &eht_cap_ie_elem->fixed);
+ &eht_cap_ie_elem->fixed,
+ sdata->vif.type ==
+ NL80211_IFTYPE_STATION);
eht_total_size += mcs_nss_size;
diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c
index c2b38370bfb1..a3830d925cc2 100644
--- a/net/mac80211/ethtool.c
+++ b/net/mac80211/ethtool.c
@@ -83,17 +83,17 @@ static void ieee80211_get_stats(struct net_device *dev,
#define ADD_STA_STATS(sta) \
do { \
- data[i++] += (sta)->rx_stats.packets; \
- data[i++] += (sta)->rx_stats.bytes; \
+ data[i++] += sinfo.rx_packets; \
+ data[i++] += sinfo.rx_bytes; \
data[i++] += (sta)->rx_stats.num_duplicates; \
data[i++] += (sta)->rx_stats.fragments; \
- data[i++] += (sta)->rx_stats.dropped; \
+ data[i++] += sinfo.rx_dropped_misc; \
\
data[i++] += sinfo.tx_packets; \
data[i++] += sinfo.tx_bytes; \
data[i++] += (sta)->status_stats.filtered; \
- data[i++] += (sta)->status_stats.retry_failed; \
- data[i++] += (sta)->status_stats.retry_count; \
+ data[i++] += sinfo.tx_failed; \
+ data[i++] += sinfo.tx_retries; \
} while (0)
/* For Managed stations, find the single station based on BSSID
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index d56890e3fabb..c14d740b0122 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1346,10 +1346,10 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
capability, 0, true);
}
-static unsigned ibss_setup_channels(struct wiphy *wiphy,
- struct ieee80211_channel **channels,
- unsigned int channels_max,
- u32 center_freq, u32 width)
+static unsigned int ibss_setup_channels(struct wiphy *wiphy,
+ struct ieee80211_channel **channels,
+ unsigned int channels_max,
+ u32 center_freq, u32 width)
{
struct ieee80211_channel *chan = NULL;
unsigned int n_chan = 0;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e192e1ec0261..9f358977a6b9 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -213,6 +213,7 @@ struct ieee80211_rx_data {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_link_data *link;
struct sta_info *sta;
+ struct link_sta_info *link_sta;
struct ieee80211_key *key;
unsigned int flags;
@@ -2184,6 +2185,8 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
* for that non-transmitting BSS is returned
* @link_id: the link ID to parse elements for, if a STA profile
* is present in the multi-link element, or -1 to ignore
+ * @from_ap: frame is received from an AP (currently used only
+ * for EHT capabilities parsing)
*/
struct ieee80211_elems_parse_params {
const u8 *start;
@@ -2193,6 +2196,7 @@ struct ieee80211_elems_parse_params {
u32 crc;
struct cfg80211_bss *bss;
int link_id;
+ bool from_ap;
};
struct ieee802_11_elems *
@@ -2382,6 +2386,7 @@ u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end);
void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps_mode,
struct sk_buff *skb);
u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef);
int ieee80211_parse_bitrates(enum nl80211_chan_width width,
@@ -2513,7 +2518,8 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype);
u8 *ieee80211_ie_build_eht_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
const struct ieee80211_sta_eht_cap *eht_cap,
- u8 *end);
+ u8 *end,
+ bool for_ap);
void
ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 95b58c5cac07..14505278073a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -406,9 +406,11 @@ static void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
ether_addr_copy(link_conf->addr,
sdata->wdev.links[link_id].addr);
+ link_conf->bssid = link_conf->addr;
WARN_ON(!(sdata->wdev.valid_links & BIT(link_id)));
break;
case NL80211_IFTYPE_STATION:
+ /* station sets the bssid in ieee80211_mgd_setup_link */
break;
default:
WARN_ON(1);
@@ -432,10 +434,19 @@ struct link_container {
static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata,
struct link_container **links)
{
+ LIST_HEAD(keys);
unsigned int link_id;
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
+ if (!links[link_id])
+ continue;
+ ieee80211_remove_link_keys(&links[link_id]->data, &keys);
+ }
+
synchronize_rcu();
+ ieee80211_free_key_list(sdata->local, &keys);
+
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
if (!links[link_id])
continue;
@@ -2267,7 +2278,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
wdev = &sdata->wdev;
sdata->dev = NULL;
- strlcpy(sdata->name, name, IFNAMSIZ);
+ strscpy(sdata->name, name, IFNAMSIZ);
ieee80211_assign_perm_addr(local, wdev->address, type);
memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 6befb578ed9e..86aac87e0211 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -344,9 +344,10 @@ static void ieee80211_pairwise_rekey(struct ieee80211_key *old,
}
}
-static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
+static void __ieee80211_set_default_key(struct ieee80211_link_data *link,
int idx, bool uni, bool multi)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_key *key = NULL;
assert_key_lock(sdata->local);
@@ -354,7 +355,7 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
if (idx >= 0 && idx < NUM_DEFAULT_KEYS) {
key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
if (!key)
- key = key_mtx_dereference(sdata->local, sdata->deflink.gtk[idx]);
+ key = key_mtx_dereference(sdata->local, link->gtk[idx]);
}
if (uni) {
@@ -365,47 +366,48 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
}
if (multi)
- rcu_assign_pointer(sdata->deflink.default_multicast_key, key);
+ rcu_assign_pointer(link->default_multicast_key, key);
ieee80211_debugfs_key_update_default(sdata);
}
-void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
+void ieee80211_set_default_key(struct ieee80211_link_data *link, int idx,
bool uni, bool multi)
{
- mutex_lock(&sdata->local->key_mtx);
- __ieee80211_set_default_key(sdata, idx, uni, multi);
- mutex_unlock(&sdata->local->key_mtx);
+ mutex_lock(&link->sdata->local->key_mtx);
+ __ieee80211_set_default_key(link, idx, uni, multi);
+ mutex_unlock(&link->sdata->local->key_mtx);
}
static void
-__ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx)
+__ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link, int idx)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_key *key = NULL;
assert_key_lock(sdata->local);
if (idx >= NUM_DEFAULT_KEYS &&
idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
- key = key_mtx_dereference(sdata->local,
- sdata->deflink.gtk[idx]);
+ key = key_mtx_dereference(sdata->local, link->gtk[idx]);
- rcu_assign_pointer(sdata->deflink.default_mgmt_key, key);
+ rcu_assign_pointer(link->default_mgmt_key, key);
ieee80211_debugfs_key_update_default(sdata);
}
-void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
+void ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link,
int idx)
{
- mutex_lock(&sdata->local->key_mtx);
- __ieee80211_set_default_mgmt_key(sdata, idx);
- mutex_unlock(&sdata->local->key_mtx);
+ mutex_lock(&link->sdata->local->key_mtx);
+ __ieee80211_set_default_mgmt_key(link, idx);
+ mutex_unlock(&link->sdata->local->key_mtx);
}
static void
-__ieee80211_set_default_beacon_key(struct ieee80211_sub_if_data *sdata, int idx)
+__ieee80211_set_default_beacon_key(struct ieee80211_link_data *link, int idx)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_key *key = NULL;
assert_key_lock(sdata->local);
@@ -413,28 +415,30 @@ __ieee80211_set_default_beacon_key(struct ieee80211_sub_if_data *sdata, int idx)
if (idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS &&
idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS +
NUM_DEFAULT_BEACON_KEYS)
- key = key_mtx_dereference(sdata->local,
- sdata->deflink.gtk[idx]);
+ key = key_mtx_dereference(sdata->local, link->gtk[idx]);
- rcu_assign_pointer(sdata->deflink.default_beacon_key, key);
+ rcu_assign_pointer(link->default_beacon_key, key);
ieee80211_debugfs_key_update_default(sdata);
}
-void ieee80211_set_default_beacon_key(struct ieee80211_sub_if_data *sdata,
+void ieee80211_set_default_beacon_key(struct ieee80211_link_data *link,
int idx)
{
- mutex_lock(&sdata->local->key_mtx);
- __ieee80211_set_default_beacon_key(sdata, idx);
- mutex_unlock(&sdata->local->key_mtx);
+ mutex_lock(&link->sdata->local->key_mtx);
+ __ieee80211_set_default_beacon_key(link, idx);
+ mutex_unlock(&link->sdata->local->key_mtx);
}
static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
- struct sta_info *sta,
- bool pairwise,
- struct ieee80211_key *old,
- struct ieee80211_key *new)
+ struct ieee80211_link_data *link,
+ struct sta_info *sta,
+ bool pairwise,
+ struct ieee80211_key *old,
+ struct ieee80211_key *new)
{
+ struct link_sta_info *link_sta = sta ? &sta->deflink : NULL;
+ int link_id;
int idx;
int ret = 0;
bool defunikey, defmultikey, defmgmtkey, defbeaconkey;
@@ -446,13 +450,36 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (new) {
idx = new->conf.keyidx;
- list_add_tail_rcu(&new->list, &sdata->key_list);
is_wep = new->conf.cipher == WLAN_CIPHER_SUITE_WEP40 ||
new->conf.cipher == WLAN_CIPHER_SUITE_WEP104;
+ link_id = new->conf.link_id;
} else {
idx = old->conf.keyidx;
is_wep = old->conf.cipher == WLAN_CIPHER_SUITE_WEP40 ||
old->conf.cipher == WLAN_CIPHER_SUITE_WEP104;
+ link_id = old->conf.link_id;
+ }
+
+ if (WARN(old && old->conf.link_id != link_id,
+ "old link ID %d doesn't match new link ID %d\n",
+ old->conf.link_id, link_id))
+ return -EINVAL;
+
+ if (link_id >= 0) {
+ if (!link) {
+ link = sdata_dereference(sdata->link[link_id], sdata);
+ if (!link)
+ return -ENOLINK;
+ }
+
+ if (sta) {
+ link_sta = rcu_dereference_protected(sta->link[link_id],
+ lockdep_is_held(&sta->local->sta_mtx));
+ if (!link_sta)
+ return -ENOLINK;
+ }
+ } else {
+ link = &sdata->deflink;
}
if ((is_wep || pairwise) && idx >= NUM_DEFAULT_KEYS)
@@ -482,6 +509,9 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (ret)
return ret;
+ if (new)
+ list_add_tail_rcu(&new->list, &sdata->key_list);
+
if (sta) {
if (pairwise) {
rcu_assign_pointer(sta->ptk[idx], new);
@@ -489,7 +519,7 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
!(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX))
_ieee80211_set_tx_key(new, true);
} else {
- rcu_assign_pointer(sta->deflink.gtk[idx], new);
+ rcu_assign_pointer(link_sta->gtk[idx], new);
}
/* Only needed for transition from no key -> key.
* Still triggers unnecessary when using Extended Key ID
@@ -503,39 +533,39 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
sdata->default_unicast_key);
defmultikey = old &&
old == key_mtx_dereference(sdata->local,
- sdata->deflink.default_multicast_key);
+ link->default_multicast_key);
defmgmtkey = old &&
old == key_mtx_dereference(sdata->local,
- sdata->deflink.default_mgmt_key);
+ link->default_mgmt_key);
defbeaconkey = old &&
old == key_mtx_dereference(sdata->local,
- sdata->deflink.default_beacon_key);
+ link->default_beacon_key);
if (defunikey && !new)
- __ieee80211_set_default_key(sdata, -1, true, false);
+ __ieee80211_set_default_key(link, -1, true, false);
if (defmultikey && !new)
- __ieee80211_set_default_key(sdata, -1, false, true);
+ __ieee80211_set_default_key(link, -1, false, true);
if (defmgmtkey && !new)
- __ieee80211_set_default_mgmt_key(sdata, -1);
+ __ieee80211_set_default_mgmt_key(link, -1);
if (defbeaconkey && !new)
- __ieee80211_set_default_beacon_key(sdata, -1);
+ __ieee80211_set_default_beacon_key(link, -1);
if (is_wep || pairwise)
rcu_assign_pointer(sdata->keys[idx], new);
else
- rcu_assign_pointer(sdata->deflink.gtk[idx], new);
+ rcu_assign_pointer(link->gtk[idx], new);
if (defunikey && new)
- __ieee80211_set_default_key(sdata, new->conf.keyidx,
+ __ieee80211_set_default_key(link, new->conf.keyidx,
true, false);
if (defmultikey && new)
- __ieee80211_set_default_key(sdata, new->conf.keyidx,
+ __ieee80211_set_default_key(link, new->conf.keyidx,
false, true);
if (defmgmtkey && new)
- __ieee80211_set_default_mgmt_key(sdata,
+ __ieee80211_set_default_mgmt_key(link,
new->conf.keyidx);
if (defbeaconkey && new)
- __ieee80211_set_default_beacon_key(sdata,
+ __ieee80211_set_default_beacon_key(link,
new->conf.keyidx);
}
@@ -569,6 +599,7 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.flags = 0;
key->flags = 0;
+ key->conf.link_id = -1;
key->conf.cipher = cipher;
key->conf.keyidx = idx;
key->conf.keylen = key_len;
@@ -797,9 +828,10 @@ static bool ieee80211_key_identical(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_key_link(struct ieee80211_key *key,
- struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_link_data *link,
struct sta_info *sta)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
static atomic_t key_color = ATOMIC_INIT(0);
struct ieee80211_key *old_key = NULL;
int idx = key->conf.keyidx;
@@ -827,15 +859,24 @@ int ieee80211_key_link(struct ieee80211_key *key,
(old_key && old_key->conf.cipher != key->conf.cipher))
goto out;
} else if (sta) {
- old_key = key_mtx_dereference(sdata->local,
- sta->deflink.gtk[idx]);
+ struct link_sta_info *link_sta = &sta->deflink;
+ int link_id = key->conf.link_id;
+
+ if (link_id >= 0) {
+ link_sta = rcu_dereference_protected(sta->link[link_id],
+ lockdep_is_held(&sta->local->sta_mtx));
+ if (!link_sta)
+ return -ENOLINK;
+ }
+
+ old_key = key_mtx_dereference(sdata->local, link_sta->gtk[idx]);
} else {
if (idx < NUM_DEFAULT_KEYS)
old_key = key_mtx_dereference(sdata->local,
sdata->keys[idx]);
if (!old_key)
old_key = key_mtx_dereference(sdata->local,
- sdata->deflink.gtk[idx]);
+ link->gtk[idx]);
}
/* Non-pairwise keys must also not switch the cipher on rekey */
@@ -866,7 +907,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
increment_tailroom_need_count(sdata);
- ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
+ ret = ieee80211_key_replace(sdata, link, sta, pairwise, old_key, key);
if (!ret) {
ieee80211_debugfs_key_add(key);
@@ -890,9 +931,9 @@ void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)
* Replace key with nothingness if it was ever used.
*/
if (key->sdata)
- ieee80211_key_replace(key->sdata, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ ieee80211_key_replace(key->sdata, NULL, key->sta,
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
ieee80211_key_destroy(key, delay_tailroom);
}
@@ -1019,15 +1060,45 @@ static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata,
ieee80211_debugfs_key_remove_beacon_default(sdata);
list_for_each_entry_safe(key, tmp, &sdata->key_list, list) {
- ieee80211_key_replace(key->sdata, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ ieee80211_key_replace(key->sdata, NULL, key->sta,
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
list_add_tail(&key->list, keys);
}
ieee80211_debugfs_key_update_default(sdata);
}
+void ieee80211_remove_link_keys(struct ieee80211_link_data *link,
+ struct list_head *keys)
+{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_key *key, *tmp;
+
+ mutex_lock(&local->key_mtx);
+ list_for_each_entry_safe(key, tmp, &sdata->key_list, list) {
+ if (key->conf.link_id != link->link_id)
+ continue;
+ ieee80211_key_replace(key->sdata, link, key->sta,
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
+ list_add_tail(&key->list, keys);
+ }
+ mutex_unlock(&local->key_mtx);
+}
+
+void ieee80211_free_key_list(struct ieee80211_local *local,
+ struct list_head *keys)
+{
+ struct ieee80211_key *key, *tmp;
+
+ mutex_lock(&local->key_mtx);
+ list_for_each_entry_safe(key, tmp, keys, list)
+ __ieee80211_key_destroy(key, false);
+ mutex_unlock(&local->key_mtx);
+}
+
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
bool force_synchronize)
{
@@ -1087,9 +1158,9 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
key = key_mtx_dereference(local, sta->deflink.gtk[i]);
if (!key)
continue;
- ieee80211_key_replace(key->sdata, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ ieee80211_key_replace(key->sdata, NULL, key->sta,
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
__ieee80211_key_destroy(key, key->sdata->vif.type ==
NL80211_IFTYPE_STATION);
}
@@ -1098,9 +1169,9 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
key = key_mtx_dereference(local, sta->ptk[i]);
if (!key)
continue;
- ieee80211_key_replace(key->sdata, key->sta,
- key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
- key, NULL);
+ ieee80211_key_replace(key->sdata, NULL, key->sta,
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
__ieee80211_key_destroy(key, key->sdata->vif.type ==
NL80211_IFTYPE_STATION);
}
@@ -1307,7 +1378,8 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
- err = ieee80211_key_link(key, sdata, NULL);
+ /* FIXME: this function needs to get a link ID */
+ err = ieee80211_key_link(key, &sdata->deflink, NULL);
if (err)
return ERR_PTR(err);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index e994dcea1ce3..518af24aab56 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -22,6 +22,7 @@
struct ieee80211_local;
struct ieee80211_sub_if_data;
+struct ieee80211_link_data;
struct sta_info;
/**
@@ -144,17 +145,21 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
* to make it used, free old key. On failure, also free the new key.
*/
int ieee80211_key_link(struct ieee80211_key *key,
- struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_link_data *link,
struct sta_info *sta);
int ieee80211_set_tx_key(struct ieee80211_key *key);
void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom);
void ieee80211_key_free_unused(struct ieee80211_key *key);
-void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
+void ieee80211_set_default_key(struct ieee80211_link_data *link, int idx,
bool uni, bool multi);
-void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
+void ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link,
int idx);
-void ieee80211_set_default_beacon_key(struct ieee80211_sub_if_data *sdata,
+void ieee80211_set_default_beacon_key(struct ieee80211_link_data *link,
int idx);
+void ieee80211_remove_link_keys(struct ieee80211_link_data *link,
+ struct list_head *keys);
+void ieee80211_free_key_list(struct ieee80211_local *local,
+ struct list_head *keys);
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
bool force_synchronize);
void ieee80211_free_sta_keys(struct ieee80211_local *local,
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 6991c4c479da..5a99b8f6e465 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -634,7 +634,7 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
if (!iftd)
return 0;
- ieee80211_ie_build_he_6ghz_cap(sdata, skb);
+ ieee80211_ie_build_he_6ghz_cap(sdata, sdata->deflink.smps_mode, skb);
return 0;
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 3d4ab711f0d1..00d0c433fa2b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -695,6 +695,7 @@ static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct ieee80211_supported_band *sband,
+ enum ieee80211_smps_mode smps_mode,
ieee80211_conn_flags_t conn_flags)
{
u8 *pos, *pre_he_pos;
@@ -719,7 +720,7 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
/* trim excess if any */
skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos));
- ieee80211_ie_build_he_6ghz_cap(sdata, skb);
+ ieee80211_ie_build_he_6ghz_cap(sdata, smps_mode, skb);
}
static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata,
@@ -746,11 +747,13 @@ static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata,
eht_cap_size =
2 + 1 + sizeof(eht_cap->eht_cap_elem) +
ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
- &eht_cap->eht_cap_elem) +
+ &eht_cap->eht_cap_elem,
+ false) +
ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
eht_cap->eht_cap_elem.phy_cap_info);
pos = skb_put(skb, eht_cap_size);
- ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size);
+ ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size,
+ false);
}
static void ieee80211_assoc_add_rates(struct sk_buff *skb,
@@ -1098,7 +1101,7 @@ static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata,
offset);
if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HE)) {
- ieee80211_add_he_ie(sdata, skb, sband,
+ ieee80211_add_he_ie(sdata, skb, sband, smps_mode,
assoc_data->link[link_id].conn_flags);
ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY);
}
@@ -1220,14 +1223,21 @@ static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata,
ml_elem = skb_put(skb, sizeof(*ml_elem));
ml_elem->control =
cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC |
- IEEE80211_MLC_BASIC_PRES_EML_CAPA |
IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP);
common = skb_put(skb, sizeof(*common));
common->len = sizeof(*common) +
- 2 + /* EML capabilities */
2; /* MLD capa/ops */
memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN);
- skb_put_data(skb, &eml_capa, sizeof(eml_capa));
+
+ /* add EML_CAPA only if needed, see Draft P802.11be_D2.1, 35.3.17 */
+ if (eml_capa &
+ cpu_to_le16((IEEE80211_EML_CAP_EMLSR_SUPP |
+ IEEE80211_EML_CAP_EMLMR_SUPPORT))) {
+ common->len += 2; /* EML capabilities */
+ ml_elem->control |=
+ cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA);
+ skb_put_data(skb, &eml_capa, sizeof(eml_capa));
+ }
/* need indication from userspace to support this */
mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP);
skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops));
@@ -1902,7 +1912,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
IEEE80211_QUEUE_STOP_REASON_CSA);
mutex_unlock(&local->mtx);
- cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, 0,
csa_ie.count, csa_ie.mode);
if (local->ops->channel_switch) {
@@ -3905,6 +3915,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
.len = elem_len,
.bss = cbss,
.link_id = link == &sdata->deflink ? -1 : link->link_id,
+ .from_ap = true,
};
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
@@ -4573,6 +4584,11 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
struct ieee80211_bss *bss = (void *)cbss->priv;
+ struct ieee80211_elems_parse_params parse_params = {
+ .bss = cbss,
+ .link_id = -1,
+ .from_ap = true,
+ };
struct ieee802_11_elems *elems;
const struct cfg80211_bss_ies *ies;
int ret;
@@ -4582,7 +4598,9 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
ies = rcu_dereference(cbss->ies);
- elems = ieee802_11_parse_elems(ies->data, ies->len, false, cbss);
+ parse_params.start = ies->data;
+ parse_params.len = ies->len;
+ elems = ieee802_11_parse_elems_full(&parse_params);
if (!elems) {
rcu_read_unlock();
return -ENOMEM;
@@ -4937,6 +4955,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
u16 capab_info, status_code, aid;
+ struct ieee80211_elems_parse_params parse_params = {
+ .bss = NULL,
+ .link_id = -1,
+ .from_ap = true,
+ };
struct ieee802_11_elems *elems;
int ac;
const u8 *elem_start;
@@ -4991,7 +5014,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return;
elem_len = len - (elem_start - (u8 *)mgmt);
- elems = ieee802_11_parse_elems(elem_start, elem_len, false, NULL);
+ parse_params.start = elem_start;
+ parse_params.len = elem_len;
+ elems = ieee802_11_parse_elems_full(&parse_params);
if (!elems)
goto notify_driver;
@@ -5124,7 +5149,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
resp.req_ies = ifmgd->assoc_req_ies;
resp.req_ies_len = ifmgd->assoc_req_ies_len;
if (sdata->vif.valid_links)
- resp.ap_mld_addr = assoc_data->ap_addr;
+ resp.ap_mld_addr = sdata->vif.cfg.ap_addr;
cfg80211_rx_assoc_resp(sdata->dev, &resp);
notify_driver:
drv_mgd_complete_tx(sdata->local, sdata, &info);
@@ -5356,6 +5381,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
u32 ncrc = 0;
u8 *bssid, *variable = mgmt->u.beacon.variable;
u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
+ struct ieee80211_elems_parse_params parse_params = {
+ .link_id = -1,
+ .from_ap = true,
+ };
sdata_assert_lock(sdata);
@@ -5374,6 +5403,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
if (baselen > len)
return;
+ parse_params.start = variable;
+ parse_params.len = len - baselen;
+
rcu_read_lock();
chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
if (!chanctx_conf) {
@@ -5392,8 +5424,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
!WARN_ON(sdata->vif.valid_links) &&
ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->link[0].bss)) {
- elems = ieee802_11_parse_elems(variable, len - baselen, false,
- ifmgd->assoc_data->link[0].bss);
+ parse_params.bss = ifmgd->assoc_data->link[0].bss;
+ elems = ieee802_11_parse_elems_full(&parse_params);
if (!elems)
return;
@@ -5459,9 +5491,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
*/
if (!ieee80211_is_s1g_beacon(hdr->frame_control))
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
- elems = ieee802_11_parse_elems_crc(variable, len - baselen,
- false, care_about_ies, ncrc,
- link->u.mgd.bss);
+ parse_params.bss = link->u.mgd.bss;
+ parse_params.filter = care_about_ies;
+ parse_params.crc = ncrc;
+ elems = ieee802_11_parse_elems_full(&parse_params);
if (!elems)
return;
ncrc = elems->crc;
@@ -5671,6 +5704,13 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
sdata_lock(sdata);
+ if (rx_status->link_valid) {
+ link = sdata_dereference(sdata->link[rx_status->link_id],
+ sdata);
+ if (!link)
+ goto out;
+ }
+
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_BEACON:
ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
@@ -5747,6 +5787,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
}
break;
}
+out:
sdata_unlock(sdata);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 57df21e2170a..cc139fe5fb78 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -215,9 +215,19 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
}
static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
+ int link_id,
struct sta_info *sta,
struct sk_buff *skb)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
+ if (link_id >= 0) {
+ status->link_valid = 1;
+ status->link_id = link_id;
+ } else {
+ status->link_valid = 0;
+ }
+
skb_queue_tail(&sdata->skb_queue, skb);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
if (sta)
@@ -225,11 +235,12 @@ static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
}
static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
+ int link_id,
struct sta_info *sta,
struct sk_buff *skb)
{
skb->protocol = 0;
- __ieee80211_queue_skb_to_iface(sdata, sta, skb);
+ __ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb);
}
static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
@@ -272,7 +283,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
if (!skb)
return;
- ieee80211_queue_skb_to_iface(sdata, NULL, skb);
+ ieee80211_queue_skb_to_iface(sdata, -1, NULL, skb);
}
/*
@@ -1394,7 +1405,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
- ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb);
+ ieee80211_queue_skb_to_iface(rx->sdata, rx->link_id, NULL, skb);
return;
}
@@ -1854,7 +1865,6 @@ static struct ieee80211_key *
ieee80211_rx_get_bigtk(struct ieee80211_rx_data *rx, int idx)
{
struct ieee80211_key *key = NULL;
- struct ieee80211_sub_if_data *sdata = rx->sdata;
int idx2;
/* Make sure key gets set if either BIGTK key index is set so that
@@ -1873,14 +1883,14 @@ ieee80211_rx_get_bigtk(struct ieee80211_rx_data *rx, int idx)
idx2 = idx - 1;
}
- if (rx->sta)
- key = rcu_dereference(rx->sta->deflink.gtk[idx]);
+ if (rx->link_sta)
+ key = rcu_dereference(rx->link_sta->gtk[idx]);
if (!key)
- key = rcu_dereference(sdata->deflink.gtk[idx]);
- if (!key && rx->sta)
- key = rcu_dereference(rx->sta->deflink.gtk[idx2]);
+ key = rcu_dereference(rx->link->gtk[idx]);
+ if (!key && rx->link_sta)
+ key = rcu_dereference(rx->link_sta->gtk[idx2]);
if (!key)
- key = rcu_dereference(sdata->deflink.gtk[idx2]);
+ key = rcu_dereference(rx->link->gtk[idx2]);
return key;
}
@@ -1986,15 +1996,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (mmie_keyidx < NUM_DEFAULT_KEYS ||
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
return RX_DROP_MONITOR; /* unexpected BIP keyidx */
- if (rx->sta) {
+ if (rx->link_sta) {
if (ieee80211_is_group_privacy_action(skb) &&
test_sta_flag(rx->sta, WLAN_STA_MFP))
return RX_DROP_MONITOR;
- rx->key = rcu_dereference(rx->sta->deflink.gtk[mmie_keyidx]);
+ rx->key = rcu_dereference(rx->link_sta->gtk[mmie_keyidx]);
}
if (!rx->key)
- rx->key = rcu_dereference(rx->sdata->deflink.gtk[mmie_keyidx]);
+ rx->key = rcu_dereference(rx->link->gtk[mmie_keyidx]);
} else if (!ieee80211_has_protected(fc)) {
/*
* The frame was not protected, so skip decryption. However, we
@@ -2003,25 +2013,24 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
* have been expected.
*/
struct ieee80211_key *key = NULL;
- struct ieee80211_sub_if_data *sdata = rx->sdata;
int i;
if (ieee80211_is_beacon(fc)) {
key = ieee80211_rx_get_bigtk(rx, -1);
} else if (ieee80211_is_mgmt(fc) &&
is_multicast_ether_addr(hdr->addr1)) {
- key = rcu_dereference(rx->sdata->deflink.default_mgmt_key);
+ key = rcu_dereference(rx->link->default_mgmt_key);
} else {
- if (rx->sta) {
+ if (rx->link_sta) {
for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
- key = rcu_dereference(rx->sta->deflink.gtk[i]);
+ key = rcu_dereference(rx->link_sta->gtk[i]);
if (key)
break;
}
}
if (!key) {
for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
- key = rcu_dereference(sdata->deflink.gtk[i]);
+ key = rcu_dereference(rx->link->gtk[i]);
if (key)
break;
}
@@ -2050,13 +2059,13 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE;
/* check per-station GTK first, if multicast packet */
- if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
- rx->key = rcu_dereference(rx->sta->deflink.gtk[keyidx]);
+ if (is_multicast_ether_addr(hdr->addr1) && rx->link_sta)
+ rx->key = rcu_dereference(rx->link_sta->gtk[keyidx]);
/* if not found, try default key */
if (!rx->key) {
if (is_multicast_ether_addr(hdr->addr1))
- rx->key = rcu_dereference(rx->sdata->deflink.gtk[keyidx]);
+ rx->key = rcu_dereference(rx->link->gtk[keyidx]);
if (!rx->key)
rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
@@ -3046,7 +3055,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
rx->skb->protocol = cpu_to_be16(ETH_P_TDLS);
- __ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
+ __ieee80211_queue_skb_to_iface(sdata, rx->link_id,
+ rx->sta, rx->skb);
return RX_QUEUED;
}
}
@@ -3636,7 +3646,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_QUEUED;
queue:
- ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
+ ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
return RX_QUEUED;
}
@@ -3794,7 +3804,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
/* for now only beacons are ext, so queue them */
- ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
+ ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
return RX_QUEUED;
}
@@ -3851,7 +3861,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
}
- ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
+ ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
return RX_QUEUED;
}
@@ -4508,6 +4518,15 @@ void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->sta_mtx);
}
+static bool
+ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
+{
+ if (!sta->mlo)
+ return false;
+
+ return !!(sta->valid_links & BIT(link_id));
+}
+
static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
struct ieee80211_fast_rx *fast_rx,
int orig_len)
@@ -4515,19 +4534,30 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
struct ieee80211_sta_rx_stats *stats;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
struct sta_info *sta = rx->sta;
+ struct link_sta_info *link_sta;
struct sk_buff *skb = rx->skb;
void *sa = skb->data + ETH_ALEN;
void *da = skb->data;
- stats = &sta->deflink.rx_stats;
+ if (rx->link_id >= 0) {
+ link_sta = rcu_dereference(sta->link[rx->link_id]);
+ if (WARN_ON_ONCE(!link_sta)) {
+ dev_kfree_skb(rx->skb);
+ return;
+ }
+ } else {
+ link_sta = &sta->deflink;
+ }
+
+ stats = &link_sta->rx_stats;
if (fast_rx->uses_rss)
- stats = this_cpu_ptr(sta->deflink.pcpu_rx_stats);
+ stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
/* statistics part of ieee80211_rx_h_sta_process() */
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
stats->last_signal = status->signal;
if (!fast_rx->uses_rss)
- ewma_signal_add(&sta->deflink.rx_stats_avg.signal,
+ ewma_signal_add(&link_sta->rx_stats_avg.signal,
-status->signal);
}
@@ -4543,7 +4573,7 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
stats->chain_signal_last[i] = signal;
if (!fast_rx->uses_rss)
- ewma_signal_add(&sta->deflink.rx_stats_avg.chain_signal[i],
+ ewma_signal_add(&link_sta->rx_stats_avg.chain_signal[i],
-signal);
}
}
@@ -4619,7 +4649,8 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
} addrs __aligned(2);
- struct ieee80211_sta_rx_stats *stats = &sta->deflink.rx_stats;
+ struct link_sta_info *link_sta;
+ struct ieee80211_sta_rx_stats *stats;
/* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
* to a common data structure; drivers can implement that per queue
@@ -4720,8 +4751,19 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
return true;
drop:
dev_kfree_skb(skb);
+
+ if (rx->link_id >= 0) {
+ link_sta = rcu_dereference(sta->link[rx->link_id]);
+ if (!link_sta)
+ return true;
+ } else {
+ link_sta = &sta->deflink;
+ }
+
if (fast_rx->uses_rss)
- stats = this_cpu_ptr(sta->deflink.pcpu_rx_stats);
+ stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
+ else
+ stats = &link_sta->rx_stats;
stats->dropped++;
return true;
@@ -4769,7 +4811,17 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
if (!link)
return true;
rx->link = link;
+
+ if (rx->sta) {
+ rx->link_sta =
+ rcu_dereference(rx->sta->link[rx->link_id]);
+ if (!rx->link_sta)
+ return true;
+ }
} else {
+ if (rx->sta)
+ rx->link_sta = &rx->sta->deflink;
+
rx->link = &sdata->deflink;
}
@@ -4827,6 +4879,7 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
struct list_head *list)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_fast_rx *fast_rx;
struct ieee80211_rx_data rx;
@@ -4847,7 +4900,31 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
rx.sta = container_of(pubsta, struct sta_info, sta);
rx.sdata = rx.sta->sdata;
- rx.link = &rx.sdata->deflink;
+
+ if (status->link_valid &&
+ !ieee80211_rx_is_valid_sta_link_id(pubsta, status->link_id))
+ goto drop;
+
+ /*
+ * TODO: Should the frame be dropped if the right link_id is not
+ * available? Or may be it is fine in the current form to proceed with
+ * the frame processing because with frame being in 802.3 format,
+ * link_id is used only for stats purpose and updating the stats on
+ * the deflink is fine?
+ */
+ if (status->link_valid)
+ rx.link_id = status->link_id;
+
+ if (rx.link_id >= 0) {
+ struct ieee80211_link_data *link;
+
+ link = rcu_dereference(rx.sdata->link[rx.link_id]);
+ if (!link)
+ goto drop;
+ rx.link = link;
+ } else {
+ rx.link = &rx.sdata->deflink;
+ }
fast_rx = rcu_dereference(rx.sta->fast_rx);
if (!fast_rx)
@@ -4877,7 +4954,19 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
rx->sta = link_sta->sta;
rx->link_id = link_sta->link_id;
} else {
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
rx->sta = sta_info_get_bss(rx->sdata, hdr->addr2);
+ if (rx->sta) {
+ if (status->link_valid &&
+ !ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta,
+ status->link_id))
+ return false;
+
+ rx->link_id = status->link_valid ? status->link_id : -1;
+ } else {
+ rx->link_id = -1;
+ }
}
return ieee80211_prepare_and_rx_handle(rx, skb, consume);
@@ -4893,6 +4982,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct list_head *list)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
__le16 fc;
@@ -4937,10 +5027,39 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (ieee80211_is_data(fc)) {
struct sta_info *sta, *prev_sta;
+ u8 link_id = status->link_id;
if (pubsta) {
rx.sta = container_of(pubsta, struct sta_info, sta);
rx.sdata = rx.sta->sdata;
+
+ if (status->link_valid &&
+ !ieee80211_rx_is_valid_sta_link_id(pubsta, link_id))
+ goto out;
+
+ if (status->link_valid)
+ rx.link_id = status->link_id;
+
+ /*
+ * In MLO connection, fetch the link_id using addr2
+ * when the driver does not pass link_id in status.
+ * When the address translation is already performed by
+ * driver/hw, the valid link_id must be passed in
+ * status.
+ */
+
+ if (!status->link_valid && pubsta->mlo) {
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct link_sta_info *link_sta;
+
+ link_sta = link_sta_info_get_bss(rx.sdata,
+ hdr->addr2);
+ if (!link_sta)
+ goto out;
+
+ rx.link_id = link_sta->link_id;
+ }
+
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
return;
goto out;
@@ -4954,6 +5073,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
continue;
}
+ if ((status->link_valid &&
+ !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
+ link_id)) ||
+ (!status->link_valid && prev_sta->sta.mlo))
+ continue;
+
+ rx.link_id = status->link_valid ? link_id : -1;
rx.sta = prev_sta;
rx.sdata = prev_sta->sdata;
ieee80211_prepare_and_rx_handle(&rx, skb, false);
@@ -4962,6 +5088,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
}
if (prev_sta) {
+ if ((status->link_valid &&
+ !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
+ link_id)) ||
+ (!status->link_valid && prev_sta->sta.mlo))
+ goto out;
+
+ rx.link_id = status->link_valid ? link_id : -1;
rx.sta = prev_sta;
rx.sdata = prev_sta->sdata;
@@ -5104,6 +5237,9 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
}
}
+ if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
+ goto drop;
+
status->rx_flags = 0;
kcov_remote_start_common(skb_get_kcov_handle(skb));
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index cb23da9aff1e..4944d929def6 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -472,6 +472,7 @@ static void sta_info_add_link(struct sta_info *sta,
link_info->sta = sta;
link_info->link_id = link_id;
link_info->pub = link_sta;
+ link_sta->link_id = link_id;
rcu_assign_pointer(sta->link[link_id], link_info);
rcu_assign_pointer(sta->sta.link[link_id], link_sta);
}
@@ -2777,10 +2778,8 @@ int ieee80211_sta_activate_link(struct sta_info *sta, unsigned int link_id)
sta->sta.valid_links = new_links;
- if (!test_sta_flag(sta, WLAN_STA_INSERTED)) {
- ret = 0;
+ if (!test_sta_flag(sta, WLAN_STA_INSERTED))
goto hash;
- }
ret = drv_change_sta_links(sdata->local, sdata, &sta->sta,
old_links, new_links);
@@ -2799,6 +2798,7 @@ hash:
void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u16 old_links = sta->sta.valid_links;
lockdep_assert_held(&sdata->local->sta_mtx);
@@ -2806,8 +2806,7 @@ void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id)
if (test_sta_flag(sta, WLAN_STA_INSERTED))
drv_change_sta_links(sdata->local, sdata, &sta->sta,
- sta->sta.valid_links,
- sta->sta.valid_links & ~BIT(link_id));
+ old_links, sta->sta.valid_links);
sta_remove_link(sta, link_id, true);
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 45df9932d0ba..51d564c8742b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -576,6 +576,51 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
+static struct ieee80211_key *
+ieee80211_select_link_key(struct ieee80211_tx_data *tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ enum {
+ USE_NONE,
+ USE_MGMT_KEY,
+ USE_MCAST_KEY,
+ } which_key = USE_NONE;
+ struct ieee80211_link_data *link;
+ unsigned int link_id;
+
+ if (ieee80211_is_group_privacy_action(tx->skb))
+ which_key = USE_MCAST_KEY;
+ else if (ieee80211_is_mgmt(hdr->frame_control) &&
+ is_multicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_robust_mgmt_frame(tx->skb))
+ which_key = USE_MGMT_KEY;
+ else if (is_multicast_ether_addr(hdr->addr1))
+ which_key = USE_MCAST_KEY;
+ else
+ return NULL;
+
+ link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK);
+ if (link_id == IEEE80211_LINK_UNSPECIFIED) {
+ link = &tx->sdata->deflink;
+ } else {
+ link = rcu_dereference(tx->sdata->link[link_id]);
+ if (!link)
+ return NULL;
+ }
+
+ switch (which_key) {
+ case USE_NONE:
+ break;
+ case USE_MGMT_KEY:
+ return rcu_dereference(link->default_mgmt_key);
+ case USE_MCAST_KEY:
+ return rcu_dereference(link->default_multicast_key);
+ }
+
+ return NULL;
+}
+
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
{
@@ -591,16 +636,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (tx->sta &&
(key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))
tx->key = key;
- else if (ieee80211_is_group_privacy_action(tx->skb) &&
- (key = rcu_dereference(tx->sdata->deflink.default_multicast_key)))
- tx->key = key;
- else if (ieee80211_is_mgmt(hdr->frame_control) &&
- is_multicast_ether_addr(hdr->addr1) &&
- ieee80211_is_robust_mgmt_frame(tx->skb) &&
- (key = rcu_dereference(tx->sdata->deflink.default_mgmt_key)))
- tx->key = key;
- else if (is_multicast_ether_addr(hdr->addr1) &&
- (key = rcu_dereference(tx->sdata->deflink.default_multicast_key)))
+ else if ((key = ieee80211_select_link_key(tx)))
tx->key = key;
else if (!is_multicast_ether_addr(hdr->addr1) &&
(key = rcu_dereference(tx->sdata->default_unicast_key)))
@@ -2640,7 +2676,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
goto free;
}
memcpy(hdr.addr2, link->conf->addr, ETH_ALEN);
- } else if (link_id == IEEE80211_LINK_UNSPECIFIED) {
+ } else if (link_id == IEEE80211_LINK_UNSPECIFIED ||
+ (sta && sta->sta.mlo)) {
memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
} else {
struct ieee80211_bss_conf *conf;
@@ -3735,8 +3772,8 @@ begin:
!test_sta_flag(tx.sta, WLAN_STA_AUTHORIZED) &&
(!(info->control.flags &
IEEE80211_TX_CTRL_PORT_CTRL_PROTO) ||
- !ether_addr_equal(tx.sdata->vif.addr,
- hdr->addr2)))) {
+ !ieee80211_is_our_addr(tx.sdata, hdr->addr2,
+ NULL)))) {
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
ieee80211_free_txskb(&local->hw, skb);
goto begin;
@@ -5061,6 +5098,8 @@ ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
rate_control_get_rate(sdata, NULL, &txrc);
info->control.vif = vif;
+ info->control.flags |= u32_encode_bits(link->link_id,
+ IEEE80211_TX_CTRL_MLO_LINK);
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_ASSIGN_SEQ |
IEEE80211_TX_CTL_FIRST_FRAGMENT;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 53826c663723..a292e63377c3 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -954,9 +954,11 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_queue_delayed_work);
-static void ieee80211_parse_extension_element(u32 *crc,
- const struct element *elem,
- struct ieee802_11_elems *elems)
+static void
+ieee80211_parse_extension_element(u32 *crc,
+ const struct element *elem,
+ struct ieee802_11_elems *elems,
+ struct ieee80211_elems_parse_params *params)
{
const void *data = elem->data + 1;
u8 len;
@@ -1013,7 +1015,8 @@ static void ieee80211_parse_extension_element(u32 *crc,
break;
case WLAN_EID_EXT_EHT_CAPABILITY:
if (ieee80211_eht_capa_size_ok(elems->he_cap,
- data, len)) {
+ data, len,
+ params->from_ap)) {
elems->eht_cap = data;
elems->eht_cap_len = len;
}
@@ -1385,7 +1388,7 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
case WLAN_EID_EXTENSION:
ieee80211_parse_extension_element(calc_crc ?
&crc : NULL,
- elem, elems);
+ elem, elems, params);
break;
case WLAN_EID_S1G_CAPABILITIES:
if (elen >= sizeof(*elems->s1g_capab))
@@ -2025,7 +2028,8 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata,
cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
IEEE80211_CHAN_NO_HE |
IEEE80211_CHAN_NO_EHT)) {
- pos = ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end);
+ pos = ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end,
+ sdata->vif.type == NL80211_IFTYPE_AP);
if (!pos)
goto out_err;
}
@@ -3080,6 +3084,7 @@ end:
}
void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_smps_mode smps_mode,
struct sk_buff *skb)
{
struct ieee80211_supported_band *sband;
@@ -3106,7 +3111,7 @@ void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
cap = le16_to_cpu(iftd->he_6ghz_capa.capa);
cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS;
- switch (sdata->deflink.smps_mode) {
+ switch (smps_mode) {
case IEEE80211_SMPS_AUTOMATIC:
case IEEE80211_SMPS_NUM_MODES:
WARN_ON(1);
@@ -4770,6 +4775,7 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
const struct ieee80211_sta_he_cap *he_cap;
const struct ieee80211_sta_eht_cap *eht_cap;
struct ieee80211_supported_band *sband;
+ bool is_ap;
u8 n;
sband = ieee80211_get_sband(sdata);
@@ -4781,8 +4787,12 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
if (!he_cap || !eht_cap)
return 0;
+ is_ap = iftype == NL80211_IFTYPE_AP ||
+ iftype == NL80211_IFTYPE_P2P_GO;
+
n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
- &eht_cap->eht_cap_elem);
+ &eht_cap->eht_cap_elem,
+ is_ap);
return 2 + 1 +
sizeof(he_cap->he_cap_elem) + n +
ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
@@ -4793,7 +4803,8 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
u8 *ieee80211_ie_build_eht_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
const struct ieee80211_sta_eht_cap *eht_cap,
- u8 *end)
+ u8 *end,
+ bool for_ap)
{
u8 mcs_nss_len, ppet_len;
u8 ie_len;
@@ -4804,7 +4815,8 @@ u8 *ieee80211_ie_build_eht_cap(u8 *pos,
return orig_pos;
mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
- &eht_cap->eht_cap_elem);
+ &eht_cap->eht_cap_elem,
+ for_ap);
ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
eht_cap->eht_cap_elem.phy_cap_info);
diff --git a/net/wireless/core.c b/net/wireless/core.c
index eefd6d8ff465..5b0c4d5b80cf 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -860,6 +860,9 @@ int wiphy_register(struct wiphy *wiphy)
for (i = 0; i < sband->n_iftype_data; i++) {
const struct ieee80211_sband_iftype_data *iftd;
+ bool has_ap, has_non_ap;
+ u32 ap_bits = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO);
iftd = &sband->iftype_data[i];
@@ -879,6 +882,19 @@ int wiphy_register(struct wiphy *wiphy)
else
have_he = have_he &&
iftd->he_cap.has_he;
+
+ has_ap = iftd->types_mask & ap_bits;
+ has_non_ap = iftd->types_mask & ~ap_bits;
+
+ /*
+ * For EHT 20 MHz STA, the capabilities format differs
+ * but to simplify, don't check 20 MHz but rather check
+ * only if AP and non-AP were mentioned at the same time,
+ * reject if so.
+ */
+ if (WARN_ON(iftd->eht_cap.has_eht &&
+ has_ap && has_non_ap))
+ return -EINVAL;
}
if (WARN_ON(!have_he && band == NL80211_BAND_6GHZ))
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 4935f94d1acc..edd062f104f4 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -171,7 +171,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
*/
if (rdev->ops->del_key)
for (i = 0; i < 6; i++)
- rdev_del_key(rdev, dev, i, false, NULL);
+ rdev_del_key(rdev, dev, -1, i, false, NULL);
if (wdev->u.ibss.current_bss) {
cfg80211_unhold_bss(wdev->u.ibss.current_bss);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2705e3ee8fc4..dad88d231d56 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1545,7 +1545,6 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
return -ENOLINK;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
- /* for MLO, require driver validation of the link ID */
if (wdev->connected)
return 0;
return -ENOLINK;
@@ -1821,10 +1820,15 @@ nl80211_send_iftype_data(struct sk_buff *msg,
if (eht_cap->has_eht && he_cap->has_he) {
u8 mcs_nss_size, ppe_thresh_size;
u16 ppe_thres_hdr;
+ bool is_ap;
+
+ is_ap = iftdata->types_mask & BIT(NL80211_IFTYPE_AP) ||
+ iftdata->types_mask & BIT(NL80211_IFTYPE_P2P_GO);
mcs_nss_size =
ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
- &eht_cap->eht_cap_elem);
+ &eht_cap->eht_cap_elem,
+ is_ap);
ppe_thres_hdr = get_unaligned_le16(&eht_cap->eht_ppe_thres[0]);
ppe_thresh_size =
@@ -3476,8 +3480,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (result)
goto out;
- result = rdev_set_txq_params(rdev, netdev,
- &txq_params);
+ txq_params.link_id =
+ nl80211_link_id_or_invalid(info->attrs);
+
+ wdev_lock(netdev->ieee80211_ptr);
+ if (txq_params.link_id >= 0 &&
+ !(netdev->ieee80211_ptr->valid_links &
+ BIT(txq_params.link_id)))
+ result = -ENOLINK;
+ else if (txq_params.link_id >= 0 &&
+ !netdev->ieee80211_ptr->valid_links)
+ result = -EINVAL;
+ else
+ result = rdev_set_txq_params(rdev, netdev,
+ &txq_params);
+ wdev_unlock(netdev->ieee80211_ptr);
if (result)
goto out;
}
@@ -3848,12 +3865,19 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
for_each_valid_link(wdev, link_id) {
struct nlattr *link = nla_nest_start(msg, link_id + 1);
+ struct cfg80211_chan_def chandef = {};
+ int ret;
if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
goto nla_put_failure;
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
wdev->links[link_id].addr))
goto nla_put_failure;
+
+ ret = rdev_get_channel(rdev, wdev, link_id, &chandef);
+ if (ret == 0 && nl80211_send_chandef(msg, &chandef))
+ goto nla_put_failure;
+
nla_nest_end(msg, link);
}
@@ -4320,6 +4344,38 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
return rdev_set_noack_map(rdev, dev, noack_map);
}
+static int nl80211_validate_key_link_id(struct genl_info *info,
+ struct wireless_dev *wdev,
+ int link_id, bool pairwise)
+{
+ if (pairwise) {
+ if (link_id != -1) {
+ GENL_SET_ERR_MSG(info,
+ "link ID not allowed for pairwise key");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ if (wdev->valid_links) {
+ if (link_id == -1) {
+ GENL_SET_ERR_MSG(info,
+ "link ID must for MLO group key");
+ return -EINVAL;
+ }
+ if (!(wdev->valid_links & BIT(link_id))) {
+ GENL_SET_ERR_MSG(info, "invalid link ID for MLO group key");
+ return -EINVAL;
+ }
+ } else if (link_id != -1) {
+ GENL_SET_ERR_MSG(info, "link ID not allowed for non-MLO group key");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
struct get_key_cookie {
struct sk_buff *msg;
int error;
@@ -4381,13 +4437,15 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
void *hdr;
struct sk_buff *msg;
bool bigtk_support = false;
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
if (wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION))
bigtk_support = true;
- if ((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION ||
- dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
bigtk_support = true;
@@ -4439,8 +4497,12 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
goto nla_put_failure;
- err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
- get_key_callback);
+ err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise);
+ if (err)
+ goto free_msg;
+
+ err = rdev_get_key(rdev, dev, link_id, key_idx, pairwise, mac_addr,
+ &cookie, get_key_callback);
if (err)
goto free_msg;
@@ -4464,6 +4526,8 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
struct key_parse key;
int err;
struct net_device *dev = info->user_ptr[1];
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
err = nl80211_parse_key(info, &key);
if (err)
@@ -4479,7 +4543,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
!(key.p.mode == NL80211_KEY_SET_TX))
return -EINVAL;
- wdev_lock(dev->ieee80211_ptr);
+ wdev_lock(wdev);
if (key.def) {
if (!rdev->ops->set_default_key) {
@@ -4487,18 +4551,22 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(wdev);
+ if (err)
+ goto out;
+
+ err = nl80211_validate_key_link_id(info, wdev, link_id, false);
if (err)
goto out;
- err = rdev_set_default_key(rdev, dev, key.idx,
- key.def_uni, key.def_multi);
+ err = rdev_set_default_key(rdev, dev, link_id, key.idx,
+ key.def_uni, key.def_multi);
if (err)
goto out;
#ifdef CONFIG_CFG80211_WEXT
- dev->ieee80211_ptr->wext.default_key = key.idx;
+ wdev->wext.default_key = key.idx;
#endif
} else if (key.defmgmt) {
if (key.def_uni || !key.def_multi) {
@@ -4511,16 +4579,20 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(wdev);
if (err)
goto out;
- err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
+ err = nl80211_validate_key_link_id(info, wdev, link_id, false);
+ if (err)
+ goto out;
+
+ err = rdev_set_default_mgmt_key(rdev, dev, link_id, key.idx);
if (err)
goto out;
#ifdef CONFIG_CFG80211_WEXT
- dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
+ wdev->wext.default_mgmt_key = key.idx;
#endif
} else if (key.defbeacon) {
if (key.def_uni || !key.def_multi) {
@@ -4533,11 +4605,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ err = nl80211_key_allowed(wdev);
if (err)
goto out;
- err = rdev_set_default_beacon_key(rdev, dev, key.idx);
+ err = nl80211_validate_key_link_id(info, wdev, link_id, false);
+ if (err)
+ goto out;
+
+ err = rdev_set_default_beacon_key(rdev, dev, link_id, key.idx);
if (err)
goto out;
} else if (key.p.mode == NL80211_KEY_SET_TX &&
@@ -4553,14 +4629,18 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = rdev_add_key(rdev, dev, key.idx,
+ err = nl80211_validate_key_link_id(info, wdev, link_id, true);
+ if (err)
+ goto out;
+
+ err = rdev_add_key(rdev, dev, link_id, key.idx,
NL80211_KEYTYPE_PAIRWISE,
mac_addr, &key.p);
} else {
err = -EINVAL;
}
out:
- wdev_unlock(dev->ieee80211_ptr);
+ wdev_unlock(wdev);
return err;
}
@@ -4572,6 +4652,8 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
struct key_parse key;
const u8 *mac_addr = NULL;
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
err = nl80211_parse_key(info, &key);
if (err)
@@ -4613,18 +4695,23 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
- wdev_lock(dev->ieee80211_ptr);
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ wdev_lock(wdev);
+ err = nl80211_key_allowed(wdev);
if (err)
GENL_SET_ERR_MSG(info, "key not allowed");
+
+ if (!err)
+ err = nl80211_validate_key_link_id(info, wdev, link_id,
+ key.type == NL80211_KEYTYPE_PAIRWISE);
+
if (!err) {
- err = rdev_add_key(rdev, dev, key.idx,
+ err = rdev_add_key(rdev, dev, link_id, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr, &key.p);
if (err)
GENL_SET_ERR_MSG(info, "key addition failed");
}
- wdev_unlock(dev->ieee80211_ptr);
+ wdev_unlock(wdev);
return err;
}
@@ -4636,6 +4723,8 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
u8 *mac_addr = NULL;
struct key_parse key;
+ int link_id = nl80211_link_id_or_invalid(info->attrs);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
err = nl80211_parse_key(info, &key);
if (err)
@@ -4663,27 +4752,31 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->del_key)
return -EOPNOTSUPP;
- wdev_lock(dev->ieee80211_ptr);
- err = nl80211_key_allowed(dev->ieee80211_ptr);
+ wdev_lock(wdev);
+ err = nl80211_key_allowed(wdev);
if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
if (!err)
- err = rdev_del_key(rdev, dev, key.idx,
+ err = nl80211_validate_key_link_id(info, wdev, link_id,
+ key.type == NL80211_KEYTYPE_PAIRWISE);
+
+ if (!err)
+ err = rdev_del_key(rdev, dev, link_id, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr);
#ifdef CONFIG_CFG80211_WEXT
if (!err) {
- if (key.idx == dev->ieee80211_ptr->wext.default_key)
- dev->ieee80211_ptr->wext.default_key = -1;
- else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
- dev->ieee80211_ptr->wext.default_mgmt_key = -1;
+ if (key.idx == wdev->wext.default_key)
+ wdev->wext.default_key = -1;
+ else if (key.idx == wdev->wext.default_mgmt_key)
+ wdev->wext.default_mgmt_key = -1;
}
#endif
- wdev_unlock(dev->ieee80211_ptr);
+ wdev_unlock(wdev);
return err;
}
@@ -5587,7 +5680,7 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
params->eht_cap = (void *)(cap->data + 1);
if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_cap,
(const u8 *)params->eht_cap,
- cap->datalen - 1))
+ cap->datalen - 1, true))
return -EINVAL;
}
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len);
@@ -6816,7 +6909,8 @@ static int nl80211_set_station_tdls(struct genl_info *info,
if (!ieee80211_eht_capa_size_ok((const u8 *)params->link_sta_params.he_capa,
(const u8 *)params->link_sta_params.eht_capa,
- params->link_sta_params.eht_capa_len))
+ params->link_sta_params.eht_capa_len,
+ false))
return -EINVAL;
}
}
@@ -7127,7 +7221,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (!ieee80211_eht_capa_size_ok((const u8 *)params.link_sta_params.he_capa,
(const u8 *)params.link_sta_params.eht_capa,
- params.link_sta_params.eht_capa_len))
+ params.link_sta_params.eht_capa_len,
+ false))
return -EINVAL;
}
}
@@ -15887,7 +15982,8 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
if (!ieee80211_eht_capa_size_ok((const u8 *)params.he_capa,
(const u8 *)params.eht_capa,
- params.eht_capa_len))
+ params.eht_capa_len,
+ false))
return -EINVAL;
}
}
@@ -18836,11 +18932,13 @@ EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
+ unsigned int link_id,
struct cfg80211_chan_def *chandef,
gfp_t gfp,
enum nl80211_commands notif,
u8 count, bool quiet)
{
+ struct wireless_dev *wdev = netdev->ieee80211_ptr;
struct sk_buff *msg;
void *hdr;
@@ -18857,6 +18955,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
goto nla_put_failure;
+ if (wdev->valid_links &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto nla_put_failure;
+
if (nl80211_send_chandef(msg, chandef))
goto nla_put_failure;
@@ -18916,22 +19018,26 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
cfg80211_sched_dfs_chan_update(rdev);
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL,
NL80211_CMD_CH_SWITCH_NOTIFY, 0, false);
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
void cfg80211_ch_switch_started_notify(struct net_device *dev,
struct cfg80211_chan_def *chandef,
- u8 count, bool quiet)
+ unsigned int link_id, u8 count,
+ bool quiet)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- trace_cfg80211_ch_switch_started_notify(dev, chandef);
+ ASSERT_WDEV_LOCK(wdev);
+ WARN_INVALID_LINK_ID(wdev, link_id);
+
+ trace_cfg80211_ch_switch_started_notify(dev, chandef, link_id);
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ nl80211_ch_switch_notify(rdev, dev, link_id, chandef, GFP_KERNEL,
NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
count, quiet);
}
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 40915a82da73..13b209a8db28 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -77,65 +77,69 @@ rdev_change_virtual_intf(struct cfg80211_registered_device *rdev,
}
static inline int rdev_add_key(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, u8 key_index,
- bool pairwise, const u8 *mac_addr,
+ struct net_device *netdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params)
{
int ret;
- trace_rdev_add_key(&rdev->wiphy, netdev, key_index, pairwise,
+ trace_rdev_add_key(&rdev->wiphy, netdev, link_id, key_index, pairwise,
mac_addr, params->mode);
- ret = rdev->ops->add_key(&rdev->wiphy, netdev, key_index, pairwise,
- mac_addr, params);
+ ret = rdev->ops->add_key(&rdev->wiphy, netdev, link_id, key_index,
+ pairwise, mac_addr, params);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_get_key(struct cfg80211_registered_device *rdev, struct net_device *netdev,
- u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie,
+ int link_id, u8 key_index, bool pairwise, const u8 *mac_addr,
+ void *cookie,
void (*callback)(void *cookie, struct key_params*))
{
int ret;
- trace_rdev_get_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
- ret = rdev->ops->get_key(&rdev->wiphy, netdev, key_index, pairwise,
- mac_addr, cookie, callback);
+ trace_rdev_get_key(&rdev->wiphy, netdev, link_id, key_index, pairwise,
+ mac_addr);
+ ret = rdev->ops->get_key(&rdev->wiphy, netdev, link_id, key_index,
+ pairwise, mac_addr, cookie, callback);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int rdev_del_key(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, u8 key_index,
- bool pairwise, const u8 *mac_addr)
+ struct net_device *netdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr)
{
int ret;
- trace_rdev_del_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
- ret = rdev->ops->del_key(&rdev->wiphy, netdev, key_index, pairwise,
- mac_addr);
+ trace_rdev_del_key(&rdev->wiphy, netdev, link_id, key_index, pairwise,
+ mac_addr);
+ ret = rdev->ops->del_key(&rdev->wiphy, netdev, link_id, key_index,
+ pairwise, mac_addr);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_set_default_key(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, u8 key_index, bool unicast,
- bool multicast)
+ struct net_device *netdev, int link_id, u8 key_index,
+ bool unicast, bool multicast)
{
int ret;
- trace_rdev_set_default_key(&rdev->wiphy, netdev, key_index,
+ trace_rdev_set_default_key(&rdev->wiphy, netdev, link_id, key_index,
unicast, multicast);
- ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, key_index,
- unicast, multicast);
+ ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, link_id,
+ key_index, unicast, multicast);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, u8 key_index)
+ struct net_device *netdev, int link_id, u8 key_index)
{
int ret;
- trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, key_index);
- ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev,
+ trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, link_id,
+ key_index);
+ ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev, link_id,
key_index);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
@@ -143,13 +147,15 @@ rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev,
static inline int
rdev_set_default_beacon_key(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, u8 key_index)
+ struct net_device *netdev, int link_id,
+ u8 key_index)
{
int ret;
- trace_rdev_set_default_beacon_key(&rdev->wiphy, netdev, key_index);
- ret = rdev->ops->set_default_beacon_key(&rdev->wiphy, netdev,
- key_index);
+ trace_rdev_set_default_beacon_key(&rdev->wiphy, netdev, link_id,
+ key_index);
+ ret = rdev->ops->set_default_beacon_key(&rdev->wiphy, netdev, link_id,
+ key_index);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index c7383ede794f..d5c7a5aa6853 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2389,6 +2389,10 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
+ if (!wdev->links[link].ap.beacon_interval)
+ continue;
+ chandef = wdev->links[link].ap.chandef;
+ break;
case NL80211_IFTYPE_MESH_POINT:
if (!wdev->u.mesh.beacon_interval)
continue;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 0134e5d5c81a..5382fc2003db 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -540,7 +540,7 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
memcpy(entry->bssid, pos, ETH_ALEN);
pos += ETH_ALEN;
- if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) {
+ if (length >= IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) {
memcpy(&entry->short_ssid, pos,
sizeof(entry->short_ssid));
entry->short_ssid_valid = true;
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 27fb2a0c4052..d513536617bd 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -747,6 +747,9 @@ void __cfg80211_connect_result(struct net_device *dev,
if (WARN_ON(!cr->links[link].addr))
goto out;
}
+
+ if (WARN_ON(wdev->connect_keys))
+ goto out;
}
wdev->unprot_beacon_reported = 0;
@@ -1325,7 +1328,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
max_key_idx = 7;
for (i = 0; i <= max_key_idx; i++)
- rdev_del_key(rdev, dev, i, false, NULL);
+ rdev_del_key(rdev, dev, -1, i, false, NULL);
}
rdev_set_qos_map(rdev, dev, NULL);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 10b2fd9bacb5..a405c3edbc47 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -434,13 +434,14 @@ TRACE_EVENT(rdev_change_virtual_intf,
);
DECLARE_EVENT_CLASS(key_handle,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool pairwise, const u8 *mac_addr),
- TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr),
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, link_id, key_index, pairwise, mac_addr),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
MAC_ENTRY(mac_addr)
+ __field(int, link_id)
__field(u8, key_index)
__field(bool, pairwise)
),
@@ -448,34 +449,38 @@ DECLARE_EVENT_CLASS(key_handle,
WIPHY_ASSIGN;
NETDEV_ASSIGN;
MAC_ASSIGN(mac_addr, mac_addr);
+ __entry->link_id = link_id;
__entry->key_index = key_index;
__entry->pairwise = pairwise;
),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
- WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
- BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr))
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d, "
+ "key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id,
+ __entry->key_index, BOOL_TO_STR(__entry->pairwise),
+ MAC_PR_ARG(mac_addr))
);
DEFINE_EVENT(key_handle, rdev_get_key,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool pairwise, const u8 *mac_addr),
- TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, link_id, key_index, pairwise, mac_addr)
);
DEFINE_EVENT(key_handle, rdev_del_key,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool pairwise, const u8 *mac_addr),
- TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, link_id, key_index, pairwise, mac_addr)
);
TRACE_EVENT(rdev_add_key,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool pairwise, const u8 *mac_addr, u8 mode),
- TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr, mode),
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr, u8 mode),
+ TP_ARGS(wiphy, netdev, link_id, key_index, pairwise, mac_addr, mode),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
MAC_ENTRY(mac_addr)
+ __field(int, link_id)
__field(u8, key_index)
__field(bool, pairwise)
__field(u8, mode)
@@ -484,24 +489,27 @@ TRACE_EVENT(rdev_add_key,
WIPHY_ASSIGN;
NETDEV_ASSIGN;
MAC_ASSIGN(mac_addr, mac_addr);
+ __entry->link_id = link_id;
__entry->key_index = key_index;
__entry->pairwise = pairwise;
__entry->mode = mode;
),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, "
- "mode: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
- WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
- __entry->mode, BOOL_TO_STR(__entry->pairwise),
- MAC_PR_ARG(mac_addr))
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d, "
+ "key_index: %u, mode: %u, pairwise: %s, "
+ "mac addr: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id,
+ __entry->key_index, __entry->mode,
+ BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr))
);
TRACE_EVENT(rdev_set_default_key,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
- bool unicast, bool multicast),
- TP_ARGS(wiphy, netdev, key_index, unicast, multicast),
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index, bool unicast, bool multicast),
+ TP_ARGS(wiphy, netdev, link_id, key_index, unicast, multicast),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
+ __field(int, link_id)
__field(u8, key_index)
__field(bool, unicast)
__field(bool, multicast)
@@ -509,48 +517,58 @@ TRACE_EVENT(rdev_set_default_key,
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
+ __entry->link_id = link_id;
__entry->key_index = key_index;
__entry->unicast = unicast;
__entry->multicast = multicast;
),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s",
- WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
- BOOL_TO_STR(__entry->unicast),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d, "
+ "key index: %u, unicast: %s, multicast: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id,
+ __entry->key_index, BOOL_TO_STR(__entry->unicast),
BOOL_TO_STR(__entry->multicast))
);
TRACE_EVENT(rdev_set_default_mgmt_key,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index),
- TP_ARGS(wiphy, netdev, key_index),
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index),
+ TP_ARGS(wiphy, netdev, link_id, key_index),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
+ __field(int, link_id)
__field(u8, key_index)
),
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
+ __entry->link_id = link_id;
__entry->key_index = key_index;
),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u",
- WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index)
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d, "
+ "key index: %u", WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->link_id, __entry->key_index)
);
TRACE_EVENT(rdev_set_default_beacon_key,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index),
- TP_ARGS(wiphy, netdev, key_index),
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int link_id,
+ u8 key_index),
+ TP_ARGS(wiphy, netdev, link_id, key_index),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
+ __field(int, link_id)
__field(u8, key_index)
),
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
+ __entry->link_id = link_id;
__entry->key_index = key_index;
),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u",
- WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index)
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d, "
+ "key index: %u", WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->link_id, __entry->key_index)
);
TRACE_EVENT(rdev_start_ap,
@@ -3245,18 +3263,21 @@ TRACE_EVENT(cfg80211_ch_switch_notify,
TRACE_EVENT(cfg80211_ch_switch_started_notify,
TP_PROTO(struct net_device *netdev,
- struct cfg80211_chan_def *chandef),
- TP_ARGS(netdev, chandef),
+ struct cfg80211_chan_def *chandef,
+ unsigned int link_id),
+ TP_ARGS(netdev, chandef, link_id),
TP_STRUCT__entry(
NETDEV_ENTRY
CHAN_DEF_ENTRY
+ __field(unsigned int, link_id)
),
TP_fast_assign(
NETDEV_ASSIGN;
CHAN_DEF_ASSIGN(chandef);
+ __entry->link_id = link_id;
),
- TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
- NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT ", link:%d",
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG, __entry->link_id)
);
TRACE_EVENT(cfg80211_radar_event,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 2c127951764a..0b28d00ba8f5 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -935,13 +935,13 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++) {
if (!wdev->connect_keys->params[i].cipher)
continue;
- if (rdev_add_key(rdev, dev, i, false, NULL,
+ if (rdev_add_key(rdev, dev, -1, i, false, NULL,
&wdev->connect_keys->params[i])) {
netdev_err(dev, "failed to set key %d\n", i);
continue;
}
if (wdev->connect_keys->def == i &&
- rdev_set_default_key(rdev, dev, i, true, true)) {
+ rdev_set_default_key(rdev, dev, -1, i, true, true)) {
netdev_err(dev, "failed to set defkey %d\n", i);
continue;
}
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index a9767bfe7330..ddf340bfa07a 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -470,7 +470,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
else
- err = rdev_del_key(rdev, dev, idx, pairwise,
+ err = rdev_del_key(rdev, dev, -1, idx, pairwise,
addr);
}
wdev->wext.connect.privacy = false;
@@ -509,7 +509,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (wdev->connected ||
(wdev->iftype == NL80211_IFTYPE_ADHOC &&
wdev->u.ibss.current_bss))
- err = rdev_add_key(rdev, dev, idx, pairwise, addr, params);
+ err = rdev_add_key(rdev, dev, -1, idx, pairwise, addr, params);
else if (params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
params->cipher != WLAN_CIPHER_SUITE_WEP104)
return -EINVAL;
@@ -546,7 +546,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
__cfg80211_leave_ibss(rdev, wdev->netdev, true);
rejoin = true;
}
- err = rdev_set_default_key(rdev, dev, idx, true, true);
+ err = rdev_set_default_key(rdev, dev, -1, idx, true,
+ true);
}
if (!err) {
wdev->wext.default_key = idx;
@@ -561,7 +562,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (wdev->connected ||
(wdev->iftype == NL80211_IFTYPE_ADHOC &&
wdev->u.ibss.current_bss))
- err = rdev_set_default_mgmt_key(rdev, dev, idx);
+ err = rdev_set_default_mgmt_key(rdev, dev, -1, idx);
if (!err)
wdev->wext.default_mgmt_key = idx;
return err;
@@ -632,7 +633,7 @@ static int cfg80211_wext_siwencode(struct net_device *dev,
if (wdev->connected ||
(wdev->iftype == NL80211_IFTYPE_ADHOC &&
wdev->u.ibss.current_bss))
- err = rdev_set_default_key(rdev, dev, idx, true,
+ err = rdev_set_default_key(rdev, dev, -1, idx, true,
true);
if (!err)
wdev->wext.default_key = idx;
@@ -685,6 +686,13 @@ static int cfg80211_wext_siwencodeext(struct net_device *dev,
!rdev->ops->set_default_key)
return -EOPNOTSUPP;
+ wdev_lock(wdev);
+ if (wdev->valid_links) {
+ wdev_unlock(wdev);
+ return -EOPNOTSUPP;
+ }
+ wdev_unlock(wdev);
+
switch (ext->alg) {
case IW_ENCODE_ALG_NONE:
remove = true;