summaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Makefile1
-rw-r--r--net/mac80211/cfg.c136
-rw-r--r--net/mac80211/chan.c6
-rw-r--r--net/mac80211/debugfs_netdev.c26
-rw-r--r--net/mac80211/driver-ops.c172
-rw-r--r--net/mac80211/driver-ops.h165
-rw-r--r--net/mac80211/eht.c4
-rw-r--r--net/mac80211/ethtool.c10
-rw-r--r--net/mac80211/he.c12
-rw-r--r--net/mac80211/ht.c13
-rw-r--r--net/mac80211/ibss.c8
-rw-r--r--net/mac80211/ieee80211_i.h30
-rw-r--r--net/mac80211/iface.c330
-rw-r--r--net/mac80211/key.c234
-rw-r--r--net/mac80211/key.h16
-rw-r--r--net/mac80211/link.c473
-rw-r--r--net/mac80211/main.c2
-rw-r--r--net/mac80211/mesh.c2
-rw-r--r--net/mac80211/mlme.c234
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c9
-rw-r--r--net/mac80211/rx.c278
-rw-r--r--net/mac80211/scan.c2
-rw-r--r--net/mac80211/sta_info.c109
-rw-r--r--net/mac80211/sta_info.h3
-rw-r--r--net/mac80211/tx.c118
-rw-r--r--net/mac80211/util.c67
-rw-r--r--net/mac80211/vht.c8
27 files changed, 1734 insertions, 734 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index af1df3a6bd55..b8de44da1fb8 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -16,6 +16,7 @@ mac80211-y := \
s1g.o \
ibss.o \
iface.o \
+ link.o \
rate.o \
michael.o \
tkip.o \
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index a4f6971b7a19..687b4c878d4a 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)
{
@@ -202,6 +226,10 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
if (params->use_4addr == ifmgd->use_4addr)
return 0;
+ /* FIXME: no support for 4-addr MLO yet */
+ if (sdata->vif.valid_links)
+ return -EOPNOTSUPP;
+
sdata->u.mgd.use_4addr = params->use_4addr;
if (!ifmgd->associated)
return 0;
@@ -434,10 +462,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 +476,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 +487,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 +501,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 +564,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 +573,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 +613,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 +622,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 +634,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 +645,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 +662,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 +681,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 +768,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 +1682,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 +1709,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 +2640,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 +2650,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 +3687,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 +3819,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) {
@@ -4614,6 +4701,9 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (wdev->use_4addr)
+ return -EOPNOTSUPP;
+
return ieee80211_vif_set_links(sdata, wdev->valid_links);
}
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index f247daa41563..e72cf0749d49 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -1799,6 +1799,12 @@ int ieee80211_link_use_channel(struct ieee80211_link_data *link,
lockdep_assert_held(&local->mtx);
+ if (sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(link->link_id))) {
+ ieee80211_link_update_chandef(link, chandef);
+ return 0;
+ }
+
mutex_lock(&local->chanctx_mtx);
ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 1e5b041a5cea..5b014786fd2d 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -570,6 +570,30 @@ static ssize_t ieee80211_if_parse_tsf(
}
IEEE80211_IF_FILE_RW(tsf);
+static ssize_t ieee80211_if_fmt_valid_links(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ return snprintf(buf, buflen, "0x%x\n", sdata->vif.valid_links);
+}
+IEEE80211_IF_FILE_R(valid_links);
+
+static ssize_t ieee80211_if_fmt_active_links(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ return snprintf(buf, buflen, "0x%x\n", sdata->vif.active_links);
+}
+
+static ssize_t ieee80211_if_parse_active_links(struct ieee80211_sub_if_data *sdata,
+ const char *buf, int buflen)
+{
+ u16 active_links;
+
+ if (kstrtou16(buf, 0, &active_links))
+ return -EINVAL;
+
+ return ieee80211_set_active_links(&sdata->vif, active_links) ?: buflen;
+}
+IEEE80211_IF_FILE_RW(active_links);
#ifdef CONFIG_MAC80211_MESH
IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
@@ -670,6 +694,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD_MODE(uapsd_queues, 0600);
DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
DEBUGFS_ADD_MODE(tdls_wider_bw, 0600);
+ DEBUGFS_ADD_MODE(valid_links, 0200);
+ DEBUGFS_ADD_MODE(active_links, 0600);
}
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index 9b61dc7889c2..5392ffa18270 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -192,6 +192,10 @@ int drv_conf_tx(struct ieee80211_local *local,
if (!check_sdata_in_driver(sdata))
return -EIO;
+ if (sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(link->link_id)))
+ return 0;
+
if (params->cw_min == 0 || params->cw_min > params->cw_max) {
/*
* If we can't configure hardware anyway, don't warn. We may
@@ -272,6 +276,60 @@ void drv_reset_tsf(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+int drv_assign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx *ctx)
+{
+ int ret = 0;
+
+ drv_verify_link_exists(sdata, link_conf);
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ if (sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(link_conf->link_id)))
+ return 0;
+
+ trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx);
+ if (local->ops->assign_vif_chanctx) {
+ WARN_ON_ONCE(!ctx->driver_present);
+ ret = local->ops->assign_vif_chanctx(&local->hw,
+ &sdata->vif,
+ link_conf,
+ &ctx->conf);
+ }
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+void drv_unassign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx *ctx)
+{
+ might_sleep();
+
+ drv_verify_link_exists(sdata, link_conf);
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ if (sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(link_conf->link_id)))
+ return;
+
+ trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx);
+ if (local->ops->unassign_vif_chanctx) {
+ WARN_ON_ONCE(!ctx->driver_present);
+ local->ops->unassign_vif_chanctx(&local->hw,
+ &sdata->vif,
+ link_conf,
+ &ctx->conf);
+ }
+ trace_drv_return_void(local);
+}
+
int drv_switch_vif_chanctx(struct ieee80211_local *local,
struct ieee80211_vif_chanctx_switch *vifs,
int n_vifs, enum ieee80211_chanctx_switch_mode mode)
@@ -346,3 +404,117 @@ int drv_ampdu_action(struct ieee80211_local *local,
return ret;
}
+
+void drv_link_info_changed(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *info,
+ int link_id, u64 changed)
+{
+ might_sleep();
+
+ if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED) &&
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+ sdata->vif.type != NL80211_IFTYPE_OCB))
+ return;
+
+ if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
+ sdata->vif.type == NL80211_IFTYPE_NAN ||
+ (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+ !sdata->vif.bss_conf.mu_mimo_owner &&
+ !(changed & BSS_CHANGED_TXPOWER))))
+ return;
+
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ if (sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(link_id)))
+ return;
+
+ trace_drv_link_info_changed(local, sdata, info, changed);
+ if (local->ops->link_info_changed)
+ local->ops->link_info_changed(&local->hw, &sdata->vif,
+ info, changed);
+ else if (local->ops->bss_info_changed)
+ local->ops->bss_info_changed(&local->hw, &sdata->vif,
+ info, changed);
+ trace_drv_return_void(local);
+}
+
+int drv_set_key(struct ieee80211_local *local,
+ enum set_key_cmd cmd,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int ret;
+
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ if (WARN_ON(key->link_id >= 0 && sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(key->link_id))))
+ return -ENOLINK;
+
+ trace_drv_set_key(local, cmd, sdata, sta, key);
+ ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+int drv_change_vif_links(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ u16 old_links, u16 new_links,
+ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
+{
+ int ret = -EOPNOTSUPP;
+
+ might_sleep();
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ if (old_links == new_links)
+ return 0;
+
+ trace_drv_change_vif_links(local, sdata, old_links, new_links);
+ if (local->ops->change_vif_links)
+ ret = local->ops->change_vif_links(&local->hw, &sdata->vif,
+ old_links, new_links, old);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+int drv_change_sta_links(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ u16 old_links, u16 new_links)
+{
+ int ret = -EOPNOTSUPP;
+
+ might_sleep();
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ old_links &= sdata->vif.active_links;
+ new_links &= sdata->vif.active_links;
+
+ if (old_links == new_links)
+ return 0;
+
+ trace_drv_change_sta_links(local, sdata, sta, old_links, new_links);
+ if (local->ops->change_sta_links)
+ ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta,
+ old_links, new_links);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 482f5c97a72b..81e40b0a3b16 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -165,40 +165,10 @@ static inline void drv_vif_cfg_changed(struct ieee80211_local *local,
trace_drv_return_void(local);
}
-static inline void drv_link_info_changed(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_bss_conf *info,
- int link_id, u64 changed)
-{
- might_sleep();
-
- if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
- BSS_CHANGED_BEACON_ENABLED) &&
- sdata->vif.type != NL80211_IFTYPE_AP &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
- sdata->vif.type != NL80211_IFTYPE_OCB))
- return;
-
- if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
- sdata->vif.type == NL80211_IFTYPE_NAN ||
- (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
- !sdata->vif.bss_conf.mu_mimo_owner &&
- !(changed & BSS_CHANGED_TXPOWER))))
- return;
-
- if (!check_sdata_in_driver(sdata))
- return;
-
- trace_drv_link_info_changed(local, sdata, info, changed);
- if (local->ops->link_info_changed)
- local->ops->link_info_changed(&local->hw, &sdata->vif,
- info, changed);
- else if (local->ops->bss_info_changed)
- local->ops->bss_info_changed(&local->hw, &sdata->vif,
- info, changed);
- trace_drv_return_void(local);
-}
+void drv_link_info_changed(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *info,
+ int link_id, u64 changed);
static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
struct netdev_hw_addr_list *mc_list)
@@ -256,25 +226,11 @@ static inline int drv_set_tim(struct ieee80211_local *local,
return ret;
}
-static inline int drv_set_key(struct ieee80211_local *local,
- enum set_key_cmd cmd,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key)
-{
- int ret;
-
- might_sleep();
-
- sdata = get_bss_sdata(sdata);
- if (!check_sdata_in_driver(sdata))
- return -EIO;
-
- trace_drv_set_key(local, cmd, sdata, sta, key);
- ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
- trace_drv_return_int(local, ret);
- return ret;
-}
+int drv_set_key(struct ieee80211_local *local,
+ enum set_key_cmd cmd,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
static inline void drv_update_tkip_key(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -945,52 +901,14 @@ static inline void drv_verify_link_exists(struct ieee80211_sub_if_data *sdata,
sdata_assert_lock(sdata);
}
-static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_bss_conf *link_conf,
- struct ieee80211_chanctx *ctx)
-{
- int ret = 0;
-
- drv_verify_link_exists(sdata, link_conf);
- if (!check_sdata_in_driver(sdata))
- return -EIO;
-
- trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx);
- if (local->ops->assign_vif_chanctx) {
- WARN_ON_ONCE(!ctx->driver_present);
- ret = local->ops->assign_vif_chanctx(&local->hw,
- &sdata->vif,
- link_conf,
- &ctx->conf);
- }
- trace_drv_return_int(local, ret);
-
- return ret;
-}
-
-static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_bss_conf *link_conf,
- struct ieee80211_chanctx *ctx)
-{
- might_sleep();
-
- drv_verify_link_exists(sdata, link_conf);
- if (!check_sdata_in_driver(sdata))
- return;
-
- trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx);
- if (local->ops->unassign_vif_chanctx) {
- WARN_ON_ONCE(!ctx->driver_present);
- local->ops->unassign_vif_chanctx(&local->hw,
- &sdata->vif,
- link_conf,
- &ctx->conf);
- }
- trace_drv_return_void(local);
-}
-
+int drv_assign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx *ctx);
+void drv_unassign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx *ctx);
int drv_switch_vif_chanctx(struct ieee80211_local *local,
struct ieee80211_vif_chanctx_switch *vifs,
int n_vifs, enum ieee80211_chanctx_switch_mode mode);
@@ -1552,46 +1470,13 @@ static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
return ret;
}
-static inline int drv_change_vif_links(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- u16 old_links, u16 new_links,
- struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
-{
- int ret = -EOPNOTSUPP;
-
- might_sleep();
-
- if (!check_sdata_in_driver(sdata))
- return -EIO;
-
- trace_drv_change_vif_links(local, sdata, old_links, new_links);
- if (local->ops->change_vif_links)
- ret = local->ops->change_vif_links(&local->hw, &sdata->vif,
- old_links, new_links, old);
- trace_drv_return_int(local, ret);
-
- return ret;
-}
-
-static inline int drv_change_sta_links(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links)
-{
- int ret = -EOPNOTSUPP;
-
- might_sleep();
-
- if (!check_sdata_in_driver(sdata))
- return -EIO;
-
- trace_drv_change_sta_links(local, sdata, sta, old_links, new_links);
- if (local->ops->change_sta_links)
- ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta,
- old_links, new_links);
- trace_drv_return_int(local, ret);
-
- return ret;
-}
+int drv_change_vif_links(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ u16 old_links, u16 new_links,
+ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]);
+int drv_change_sta_links(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ u16 old_links, u16 new_links);
#endif /* __MAC80211_DRIVER_OPS */
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/he.c b/net/mac80211/he.c
index d9228fd3f77a..729f261520c7 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -31,25 +31,27 @@ ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa *he_6ghz_
break;
}
- sta->sta.smps_mode = smps_mode;
+ link_sta->pub->smps_mode = smps_mode;
} else {
- sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+ link_sta->pub->smps_mode = IEEE80211_SMPS_OFF;
}
switch (le16_get_bits(he_6ghz_capa->capa,
IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
- sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
break;
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
- sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
break;
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
default:
- sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
break;
}
+ ieee80211_sta_recalc_aggregates(&sta->sta);
+
link_sta->pub->he_6ghz_capa = *he_6ghz_capa;
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 8c24817cd497..83bc41346ae7 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -241,9 +241,11 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
- sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
else
- sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
+
+ ieee80211_sta_recalc_aggregates(&sta->sta);
apply:
changed = memcmp(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap));
@@ -299,12 +301,13 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
break;
}
- if (smps_mode != sta->sta.smps_mode)
+ if (smps_mode != link_sta->pub->smps_mode)
changed = true;
- sta->sta.smps_mode = smps_mode;
+ link_sta->pub->smps_mode = smps_mode;
} else {
- sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+ link_sta->pub->smps_mode = IEEE80211_SMPS_OFF;
}
+
return changed;
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 9b283bbc7bb4..9dffc3079588 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1350,10 +1350,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..4e1d4c339f2d 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;
@@ -1080,6 +1081,10 @@ struct ieee80211_sub_if_data {
struct ieee80211_link_data deflink;
struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ /* for ieee80211_set_active_links_async() */
+ struct work_struct activate_links_work;
+ u16 desired_active_links;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
struct dentry *subdir_stations;
@@ -1810,6 +1815,7 @@ void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
u8 reason, bool tx);
void ieee80211_mgd_setup_link(struct ieee80211_link_data *link);
void ieee80211_mgd_stop_link(struct ieee80211_link_data *link);
+void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -1929,9 +1935,6 @@ void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
-int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
- u16 new_links);
-
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
bool update_bss);
@@ -1942,6 +1945,17 @@ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
}
+/* link handling */
+void ieee80211_link_setup(struct ieee80211_link_data *link);
+void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
+ int link_id,
+ struct ieee80211_link_data *link,
+ struct ieee80211_bss_conf *link_conf);
+void ieee80211_link_stop(struct ieee80211_link_data *link);
+int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
+ u16 new_links);
+void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata);
+
/* tx handling */
void ieee80211_clear_tx_pending(struct ieee80211_local *local);
void ieee80211_tx_pending(struct tasklet_struct *t);
@@ -2184,6 +2198,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 +2209,7 @@ struct ieee80211_elems_parse_params {
u32 crc;
struct cfg80211_bss *bss;
int link_id;
+ bool from_ap;
};
struct ieee802_11_elems *
@@ -2382,6 +2399,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,
@@ -2407,8 +2425,7 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
const struct ieee80211_vht_operation *oper,
const struct ieee80211_ht_operation *htop,
struct cfg80211_chan_def *chandef);
-void ieee80211_chandef_eht_oper(struct ieee80211_sub_if_data *sdata,
- const struct ieee80211_eht_operation *eht_oper,
+void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper,
bool support_160, bool support_320,
struct cfg80211_chan_def *chandef);
bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
@@ -2513,7 +2530,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..572254366a0f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -200,15 +200,73 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
return ret;
}
+static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_roc_work *roc;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *scan_sdata;
+ int ret = 0;
+
+ /* To be the most flexible here we want to only limit changing the
+ * address if the specific interface is doing offchannel work or
+ * scanning.
+ */
+ if (netif_carrier_ok(sdata->dev))
+ return -EBUSY;
+
+ mutex_lock(&local->mtx);
+
+ /* First check no ROC work is happening on this iface */
+ list_for_each_entry(roc, &local->roc_list, list) {
+ if (roc->sdata != sdata)
+ continue;
+
+ if (roc->started) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ }
+
+ /* And if this iface is scanning */
+ if (local->scanning) {
+ scan_sdata = rcu_dereference_protected(local->scan_sdata,
+ lockdep_is_held(&local->mtx));
+ if (sdata == scan_sdata)
+ ret = -EBUSY;
+ }
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ /* More interface types could be added here but changing the
+ * address while powered makes the most sense in client modes.
+ */
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+unlock:
+ mutex_unlock(&local->mtx);
+ return ret;
+}
+
static int ieee80211_change_mac(struct net_device *dev, void *addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
struct sockaddr *sa = addr;
bool check_dup = true;
+ bool live = false;
int ret;
- if (ieee80211_sdata_running(sdata))
- return -EBUSY;
+ if (ieee80211_sdata_running(sdata)) {
+ ret = ieee80211_can_powered_addr_change(sdata);
+ if (ret)
+ return ret;
+
+ live = true;
+ }
if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
@@ -218,6 +276,8 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
if (ret)
return ret;
+ if (live)
+ drv_remove_interface(local, sdata);
ret = eth_mac_addr(dev, sa);
if (ret == 0) {
@@ -225,6 +285,12 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
}
+ /* Regardless of eth_mac_addr() return we still want to add the
+ * interface back. This should not fail...
+ */
+ if (live)
+ WARN_ON(drv_add_interface(local, sdata));
+
return ret;
}
@@ -296,6 +362,11 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
nsdata->vif.type))
return -ENOTUNIQ;
+ /* No support for VLAN with MLO yet */
+ if (iftype == NL80211_IFTYPE_AP_VLAN &&
+ nsdata->wdev.use_4addr)
+ return -EOPNOTSUPP;
+
/*
* can only add VLANs to enabled APs
*/
@@ -368,246 +439,6 @@ static int ieee80211_open(struct net_device *dev)
return err;
}
-static void ieee80211_link_setup(struct ieee80211_link_data *link)
-{
- if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
- ieee80211_mgd_setup_link(link);
-}
-
-static void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
- int link_id,
- struct ieee80211_link_data *link,
- struct ieee80211_bss_conf *link_conf)
-{
- bool deflink = link_id < 0;
-
- if (link_id < 0)
- link_id = 0;
-
- rcu_assign_pointer(sdata->vif.link_conf[link_id], link_conf);
- rcu_assign_pointer(sdata->link[link_id], link);
-
- link->sdata = sdata;
- link->link_id = link_id;
- link->conf = link_conf;
- link_conf->link_id = link_id;
-
- INIT_WORK(&link->csa_finalize_work,
- ieee80211_csa_finalize_work);
- INIT_WORK(&link->color_change_finalize_work,
- ieee80211_color_change_finalize_work);
- INIT_LIST_HEAD(&link->assigned_chanctx_list);
- INIT_LIST_HEAD(&link->reserved_chanctx_list);
- INIT_DELAYED_WORK(&link->dfs_cac_timer_work,
- ieee80211_dfs_cac_timer_work);
-
- if (!deflink) {
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- ether_addr_copy(link_conf->addr,
- sdata->wdev.links[link_id].addr);
- WARN_ON(!(sdata->wdev.valid_links & BIT(link_id)));
- break;
- case NL80211_IFTYPE_STATION:
- break;
- default:
- WARN_ON(1);
- }
- }
-}
-
-static void ieee80211_link_stop(struct ieee80211_link_data *link)
-{
- if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
- ieee80211_mgd_stop_link(link);
-
- ieee80211_link_release_channel(link);
-}
-
-struct link_container {
- struct ieee80211_link_data data;
- struct ieee80211_bss_conf conf;
-};
-
-static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata,
- struct link_container **links)
-{
- unsigned int link_id;
-
- synchronize_rcu();
-
- for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
- if (!links[link_id])
- continue;
- ieee80211_link_stop(&links[link_id]->data);
- kfree(links[link_id]);
- }
-}
-
-static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata)
-{
- unsigned int i, j;
-
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- struct ieee80211_link_data *link1;
-
- link1 = sdata_dereference(sdata->link[i], sdata);
- if (!link1)
- continue;
- for (j = i + 1; j < IEEE80211_MLD_MAX_NUM_LINKS; j++) {
- struct ieee80211_link_data *link2;
-
- link2 = sdata_dereference(sdata->link[j], sdata);
- if (!link2)
- continue;
-
- if (ether_addr_equal(link1->conf->addr,
- link2->conf->addr))
- return -EALREADY;
- }
- }
-
- return 0;
-}
-
-static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
- struct link_container **to_free,
- u16 new_links)
-{
- u16 old_links = sdata->vif.valid_links;
- unsigned long add = new_links & ~old_links;
- unsigned long rem = old_links & ~new_links;
- unsigned int link_id;
- int ret;
- struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link;
- struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS];
- struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS];
- bool use_deflink = old_links == 0; /* set for error case */
-
- sdata_assert_lock(sdata);
-
- memset(to_free, 0, sizeof(links));
-
- if (old_links == new_links)
- return 0;
-
- /* if there were no old links, need to clear the pointers to deflink */
- if (!old_links)
- rem |= BIT(0);
-
- /* allocate new link structures first */
- for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
- link = kzalloc(sizeof(*link), GFP_KERNEL);
- if (!link) {
- ret = -ENOMEM;
- goto free;
- }
- links[link_id] = link;
- }
-
- /* keep track of the old pointers for the driver */
- BUILD_BUG_ON(sizeof(old) != sizeof(sdata->vif.link_conf));
- memcpy(old, sdata->vif.link_conf, sizeof(old));
- /* and for us in error cases */
- BUILD_BUG_ON(sizeof(old_data) != sizeof(sdata->link));
- memcpy(old_data, sdata->link, sizeof(old_data));
-
- /* grab old links to free later */
- for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
- if (rcu_access_pointer(sdata->link[link_id]) != &sdata->deflink) {
- /*
- * we must have allocated the data through this path so
- * we know we can free both at the same time
- */
- to_free[link_id] = container_of(rcu_access_pointer(sdata->link[link_id]),
- typeof(*links[link_id]),
- data);
- }
-
- RCU_INIT_POINTER(sdata->link[link_id], NULL);
- RCU_INIT_POINTER(sdata->vif.link_conf[link_id], NULL);
- }
-
- /* link them into data structures */
- for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
- WARN_ON(!use_deflink &&
- rcu_access_pointer(sdata->link[link_id]) == &sdata->deflink);
-
- link = links[link_id];
- ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
- ieee80211_link_setup(&link->data);
- }
-
- if (new_links == 0)
- ieee80211_link_init(sdata, -1, &sdata->deflink,
- &sdata->vif.bss_conf);
-
- sdata->vif.valid_links = new_links;
-
- ret = ieee80211_check_dup_link_addrs(sdata);
- if (!ret) {
- /* tell the driver */
- ret = drv_change_vif_links(sdata->local, sdata,
- old_links, new_links,
- old);
- }
-
- if (ret) {
- /* restore config */
- memcpy(sdata->link, old_data, sizeof(old_data));
- memcpy(sdata->vif.link_conf, old, sizeof(old));
- sdata->vif.valid_links = old_links;
- /* and free (only) the newly allocated links */
- memset(to_free, 0, sizeof(links));
- goto free;
- }
-
- /* use deflink/bss_conf again if and only if there are no more links */
- use_deflink = new_links == 0;
-
- goto deinit;
-free:
- /* if we failed during allocation, only free all */
- for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
- kfree(links[link_id]);
- links[link_id] = NULL;
- }
-deinit:
- if (use_deflink)
- ieee80211_link_init(sdata, -1, &sdata->deflink,
- &sdata->vif.bss_conf);
- return ret;
-}
-
-int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
- u16 new_links)
-{
- struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
- int ret;
-
- ret = ieee80211_vif_update_links(sdata, links, new_links);
- ieee80211_free_links(sdata, links);
-
- return ret;
-}
-
-static void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
-{
- struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
-
- /*
- * The locking here is different because when we free links
- * in the station case we need to be able to cancel_work_sync()
- * something that also takes the lock.
- */
-
- sdata_lock(sdata);
- ieee80211_vif_update_links(sdata, links, 0);
- sdata_unlock(sdata);
-
- ieee80211_free_links(sdata, links);
-}
-
static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down)
{
struct ieee80211_local *local = sdata->local;
@@ -923,6 +754,8 @@ static int ieee80211_stop(struct net_device *dev)
ieee80211_stop_mbssid(sdata);
}
+ cancel_work_sync(&sdata->activate_links_work);
+
wiphy_lock(sdata->local->hw.wiphy);
ieee80211_do_stop(sdata, true);
wiphy_unlock(sdata->local->hw.wiphy);
@@ -1893,6 +1726,15 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
ieee80211_recalc_smps(sdata, &sdata->deflink);
}
+static void ieee80211_activate_links_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ activate_links_work);
+
+ ieee80211_set_active_links(&sdata->vif, sdata->desired_active_links);
+}
+
/*
* Helper function to initialise an interface to a specific type.
*/
@@ -1930,6 +1772,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sdata->status_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+ INIT_WORK(&sdata->activate_links_work, ieee80211_activate_links_work);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
@@ -2267,7 +2110,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);
@@ -2396,6 +2239,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sdata->u.mgd.use_4addr = params->use_4addr;
ndev->features |= local->hw.netdev_features;
+ ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
ndev->hw_features |= ndev->features &
MAC80211_SUPPORTED_FEATURES_TX;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 6befb578ed9e..e8f6c1e5eabf 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -177,6 +177,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
}
}
+ if (key->conf.link_id >= 0 && sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(key->conf.link_id)))
+ return 0;
+
ret = drv_set_key(key->local, SET_KEY, sdata,
sta ? &sta->sta : NULL, &key->conf);
@@ -246,6 +250,10 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
sta = key->sta;
sdata = key->sdata;
+ if (key->conf.link_id >= 0 && sdata->vif.active_links &&
+ !(sdata->vif.active_links & BIT(key->conf.link_id)))
+ return;
+
if (!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
@@ -344,9 +352,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 +363,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 +374,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 +423,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 +458,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 +517,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 +527,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 +541,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 +607,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 +836,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 +867,26 @@ 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) {
+ ret = -ENOLINK;
+ goto out;
+ }
+ }
+
+ 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 +917,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 +941,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 +1070,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 +1168,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 +1179,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 +1388,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);
@@ -1363,3 +1445,37 @@ void ieee80211_key_replay(struct ieee80211_key_conf *keyconf)
}
}
EXPORT_SYMBOL_GPL(ieee80211_key_replay);
+
+int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata,
+ unsigned long del_links_mask,
+ unsigned long add_links_mask)
+{
+ struct ieee80211_key *key;
+ int ret;
+
+ list_for_each_entry(key, &sdata->key_list, list) {
+ if (key->conf.link_id < 0 ||
+ !(del_links_mask & BIT(key->conf.link_id)))
+ continue;
+
+ /* shouldn't happen for per-link keys */
+ WARN_ON(key->sta);
+
+ ieee80211_key_disable_hw_accel(key);
+ }
+
+ list_for_each_entry(key, &sdata->key_list, list) {
+ if (key->conf.link_id < 0 ||
+ !(add_links_mask & BIT(key->conf.link_id)))
+ continue;
+
+ /* shouldn't happen for per-link keys */
+ WARN_ON(key->sta);
+
+ ret = ieee80211_key_enable_hw_accel(key);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index e994dcea1ce3..f3df97df4b72 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,22 +145,29 @@ 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,
struct sta_info *sta);
void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata);
+int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata,
+ unsigned long del_links_mask,
+ unsigned long add_links_mask);
#define key_mtx_dereference(local, ref) \
rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
new file mode 100644
index 000000000000..e309708abae8
--- /dev/null
+++ b/net/mac80211/link.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MLO link handling
+ *
+ * Copyright (C) 2022 Intel Corporation
+ */
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "driver-ops.h"
+#include "key.h"
+
+void ieee80211_link_setup(struct ieee80211_link_data *link)
+{
+ if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
+ ieee80211_mgd_setup_link(link);
+}
+
+void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
+ int link_id,
+ struct ieee80211_link_data *link,
+ struct ieee80211_bss_conf *link_conf)
+{
+ bool deflink = link_id < 0;
+
+ if (link_id < 0)
+ link_id = 0;
+
+ rcu_assign_pointer(sdata->vif.link_conf[link_id], link_conf);
+ rcu_assign_pointer(sdata->link[link_id], link);
+
+ link->sdata = sdata;
+ link->link_id = link_id;
+ link->conf = link_conf;
+ link_conf->link_id = link_id;
+
+ INIT_WORK(&link->csa_finalize_work,
+ ieee80211_csa_finalize_work);
+ INIT_WORK(&link->color_change_finalize_work,
+ ieee80211_color_change_finalize_work);
+ INIT_LIST_HEAD(&link->assigned_chanctx_list);
+ INIT_LIST_HEAD(&link->reserved_chanctx_list);
+ INIT_DELAYED_WORK(&link->dfs_cac_timer_work,
+ ieee80211_dfs_cac_timer_work);
+
+ if (!deflink) {
+ switch (sdata->vif.type) {
+ 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);
+ }
+ }
+}
+
+void ieee80211_link_stop(struct ieee80211_link_data *link)
+{
+ if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
+ ieee80211_mgd_stop_link(link);
+
+ ieee80211_link_release_channel(link);
+}
+
+struct link_container {
+ struct ieee80211_link_data data;
+ struct ieee80211_bss_conf conf;
+};
+
+static void ieee80211_tear_down_links(struct ieee80211_sub_if_data *sdata,
+ struct link_container **links, u16 mask)
+{
+ struct ieee80211_link_data *link;
+ LIST_HEAD(keys);
+ unsigned int link_id;
+
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
+ if (!(mask & BIT(link_id)))
+ continue;
+ link = &links[link_id]->data;
+ if (link_id == 0 && !link)
+ link = &sdata->deflink;
+ if (WARN_ON(!link))
+ continue;
+ ieee80211_remove_link_keys(link, &keys);
+ ieee80211_link_stop(link);
+ }
+
+ synchronize_rcu();
+
+ ieee80211_free_key_list(sdata->local, &keys);
+}
+
+static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata,
+ struct link_container **links)
+{
+ unsigned int link_id;
+
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)
+ kfree(links[link_id]);
+}
+
+static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ struct ieee80211_link_data *link1;
+
+ link1 = sdata_dereference(sdata->link[i], sdata);
+ if (!link1)
+ continue;
+ for (j = i + 1; j < IEEE80211_MLD_MAX_NUM_LINKS; j++) {
+ struct ieee80211_link_data *link2;
+
+ link2 = sdata_dereference(sdata->link[j], sdata);
+ if (!link2)
+ continue;
+
+ if (ether_addr_equal(link1->conf->addr,
+ link2->conf->addr))
+ return -EALREADY;
+ }
+ }
+
+ return 0;
+}
+
+static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
+ u16 links)
+{
+ sdata->vif.valid_links = links;
+
+ if (!links) {
+ sdata->vif.active_links = 0;
+ return;
+ }
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP:
+ /* in an AP all links are always active */
+ sdata->vif.active_links = links;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (sdata->vif.active_links)
+ break;
+ WARN_ON(hweight16(links) > 1);
+ sdata->vif.active_links = links;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
+ struct link_container **to_free,
+ u16 new_links)
+{
+ u16 old_links = sdata->vif.valid_links;
+ u16 old_active = sdata->vif.active_links;
+ unsigned long add = new_links & ~old_links;
+ unsigned long rem = old_links & ~new_links;
+ unsigned int link_id;
+ int ret;
+ struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link;
+ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS];
+ bool use_deflink = old_links == 0; /* set for error case */
+
+ sdata_assert_lock(sdata);
+
+ memset(to_free, 0, sizeof(links));
+
+ if (old_links == new_links)
+ return 0;
+
+ /* if there were no old links, need to clear the pointers to deflink */
+ if (!old_links)
+ rem |= BIT(0);
+
+ /* allocate new link structures first */
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (!link) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ links[link_id] = link;
+ }
+
+ /* keep track of the old pointers for the driver */
+ BUILD_BUG_ON(sizeof(old) != sizeof(sdata->vif.link_conf));
+ memcpy(old, sdata->vif.link_conf, sizeof(old));
+ /* and for us in error cases */
+ BUILD_BUG_ON(sizeof(old_data) != sizeof(sdata->link));
+ memcpy(old_data, sdata->link, sizeof(old_data));
+
+ /* grab old links to free later */
+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ if (rcu_access_pointer(sdata->link[link_id]) != &sdata->deflink) {
+ /*
+ * we must have allocated the data through this path so
+ * we know we can free both at the same time
+ */
+ to_free[link_id] = container_of(rcu_access_pointer(sdata->link[link_id]),
+ typeof(*links[link_id]),
+ data);
+ }
+
+ RCU_INIT_POINTER(sdata->link[link_id], NULL);
+ RCU_INIT_POINTER(sdata->vif.link_conf[link_id], NULL);
+ }
+
+ /* link them into data structures */
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ WARN_ON(!use_deflink &&
+ rcu_access_pointer(sdata->link[link_id]) == &sdata->deflink);
+
+ link = links[link_id];
+ ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
+ ieee80211_link_setup(&link->data);
+ }
+
+ if (new_links == 0)
+ ieee80211_link_init(sdata, -1, &sdata->deflink,
+ &sdata->vif.bss_conf);
+
+ ret = ieee80211_check_dup_link_addrs(sdata);
+ if (!ret) {
+ /* for keys we will not be able to undo this */
+ ieee80211_tear_down_links(sdata, to_free, rem);
+
+ ieee80211_set_vif_links_bitmaps(sdata, new_links);
+
+ /* tell the driver */
+ ret = drv_change_vif_links(sdata->local, sdata,
+ old_links & old_active,
+ new_links & sdata->vif.active_links,
+ old);
+ }
+
+ if (ret) {
+ /* restore config */
+ memcpy(sdata->link, old_data, sizeof(old_data));
+ memcpy(sdata->vif.link_conf, old, sizeof(old));
+ ieee80211_set_vif_links_bitmaps(sdata, old_links);
+ /* and free (only) the newly allocated links */
+ memset(to_free, 0, sizeof(links));
+ goto free;
+ }
+
+ /* use deflink/bss_conf again if and only if there are no more links */
+ use_deflink = new_links == 0;
+
+ goto deinit;
+free:
+ /* if we failed during allocation, only free all */
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
+ kfree(links[link_id]);
+ links[link_id] = NULL;
+ }
+deinit:
+ if (use_deflink)
+ ieee80211_link_init(sdata, -1, &sdata->deflink,
+ &sdata->vif.bss_conf);
+ return ret;
+}
+
+int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
+ u16 new_links)
+{
+ struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
+ int ret;
+
+ ret = ieee80211_vif_update_links(sdata, links, new_links);
+ ieee80211_free_links(sdata, links);
+
+ return ret;
+}
+
+void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
+{
+ struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
+
+ /*
+ * The locking here is different because when we free links
+ * in the station case we need to be able to cancel_work_sync()
+ * something that also takes the lock.
+ */
+
+ sdata_lock(sdata);
+ ieee80211_vif_update_links(sdata, links, 0);
+ sdata_unlock(sdata);
+
+ ieee80211_free_links(sdata, links);
+}
+
+static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
+ u16 active_links)
+{
+ struct ieee80211_bss_conf *link_confs[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct ieee80211_local *local = sdata->local;
+ u16 old_active = sdata->vif.active_links;
+ unsigned long rem = old_active & ~active_links;
+ unsigned long add = active_links & ~old_active;
+ struct sta_info *sta;
+ unsigned int link_id;
+ int ret, i;
+
+ if (!ieee80211_sdata_running(sdata))
+ return -ENETDOWN;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ /* cannot activate links that don't exist */
+ if (active_links & ~sdata->vif.valid_links)
+ return -EINVAL;
+
+ /* nothing to do */
+ if (old_active == active_links)
+ return 0;
+
+ for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
+ link_confs[i] = sdata_dereference(sdata->vif.link_conf[i],
+ sdata);
+
+ if (add) {
+ sdata->vif.active_links |= active_links;
+ ret = drv_change_vif_links(local, sdata,
+ old_active,
+ sdata->vif.active_links,
+ link_confs);
+ if (ret) {
+ sdata->vif.active_links = old_active;
+ return ret;
+ }
+ }
+
+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_data *link;
+
+ link = sdata_dereference(sdata->link[link_id], sdata);
+
+ /* FIXME: kill TDLS connections on the link */
+
+ ieee80211_link_release_channel(link);
+ }
+
+ list_for_each_entry(sta, &local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+ ret = drv_change_sta_links(local, sdata, &sta->sta,
+ old_active,
+ old_active | active_links);
+ WARN_ON_ONCE(ret);
+ }
+
+ ret = ieee80211_key_switch_links(sdata, rem, add);
+ WARN_ON_ONCE(ret);
+
+ list_for_each_entry(sta, &local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+ ret = drv_change_sta_links(local, sdata, &sta->sta,
+ old_active | active_links,
+ active_links);
+ WARN_ON_ONCE(ret);
+ }
+
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_link_data *link;
+
+ link = sdata_dereference(sdata->link[link_id], sdata);
+
+ ret = ieee80211_link_use_channel(link, &link->conf->chandef,
+ IEEE80211_CHANCTX_SHARED);
+ WARN_ON_ONCE(ret);
+
+ ieee80211_link_info_change_notify(sdata, link,
+ BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE |
+ BSS_CHANGED_ERP_SLOT |
+ BSS_CHANGED_HT |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_CQM |
+ BSS_CHANGED_QOS |
+ BSS_CHANGED_TXPOWER |
+ BSS_CHANGED_BANDWIDTH |
+ BSS_CHANGED_TWT |
+ BSS_CHANGED_HE_OBSS_PD |
+ BSS_CHANGED_HE_BSS_COLOR);
+ ieee80211_mgd_set_link_qos_params(link);
+ }
+
+ old_active = sdata->vif.active_links;
+ sdata->vif.active_links = active_links;
+
+ if (rem) {
+ ret = drv_change_vif_links(local, sdata, old_active,
+ active_links, link_confs);
+ WARN_ON_ONCE(ret);
+ }
+
+ return 0;
+}
+
+int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
+ u16 old_active;
+ int ret;
+
+ sdata_lock(sdata);
+ mutex_lock(&local->sta_mtx);
+ mutex_lock(&local->mtx);
+ mutex_lock(&local->key_mtx);
+ old_active = sdata->vif.active_links;
+ if (old_active & active_links) {
+ /*
+ * if there's at least one link that stays active across
+ * the change then switch to it (to those) first, and
+ * then enable the additional links
+ */
+ ret = _ieee80211_set_active_links(sdata,
+ old_active & active_links);
+ if (!ret)
+ ret = _ieee80211_set_active_links(sdata, active_links);
+ } else {
+ /* otherwise switch directly */
+ ret = _ieee80211_set_active_links(sdata, active_links);
+ }
+ mutex_unlock(&local->key_mtx);
+ mutex_unlock(&local->mtx);
+ mutex_unlock(&local->sta_mtx);
+ sdata_unlock(sdata);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_active_links);
+
+void ieee80211_set_active_links_async(struct ieee80211_vif *vif,
+ u16 active_links)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (!ieee80211_sdata_running(sdata))
+ return;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return;
+
+ /* cannot activate links that don't exist */
+ if (active_links & ~sdata->vif.valid_links)
+ return;
+
+ /* nothing to do */
+ if (sdata->vif.active_links == active_links)
+ return;
+
+ sdata->desired_active_links = active_links;
+ schedule_work(&sdata->activate_links_work);
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_active_links_async);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 5b1c47ed0cc0..46f3eddc2388 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -699,6 +699,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_SCAN_FREQ_KHZ);
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE);
if (!ops->hw_scan) {
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
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 fc764984d687..54b8d5065bbd 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -314,7 +314,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
struct cfg80211_chan_def eht_chandef = *chandef;
- ieee80211_chandef_eht_oper(sdata, eht_oper,
+ ieee80211_chandef_eht_oper(eht_oper,
eht_chandef.width ==
NL80211_CHAN_WIDTH_160,
false, &eht_chandef);
@@ -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));
@@ -1536,8 +1546,9 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
struct ieee80211_hdr_3addr *nullfunc;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif,
- !ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP));
+ skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, -1,
+ !ieee80211_hw_check(&local->hw,
+ DOESNT_SUPPORT_QOS_NDP));
if (!skb)
return;
@@ -1902,7 +1913,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) {
@@ -2435,6 +2446,29 @@ static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
ieee80211_sta_handle_tspec_ac_params(sdata);
}
+void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link)
+{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_tx_queue_params *params = link->tx_conf;
+ u8 ac;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ mlme_dbg(sdata,
+ "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
+ ac, params[ac].acm,
+ params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
+ params[ac].txop, params[ac].uapsd,
+ ifmgd->tx_tspec[ac].downgraded);
+ if (!ifmgd->tx_tspec[ac].downgraded &&
+ drv_conf_tx(local, link, ac, &params[ac]))
+ link_err(link,
+ "failed to set TX queue parameters for AC %d\n",
+ ac);
+ }
+}
+
/* MLME */
static bool
ieee80211_sta_wmm_params(struct ieee80211_local *local,
@@ -2566,20 +2600,10 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local,
}
}
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- mlme_dbg(sdata,
- "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
- ac, params[ac].acm,
- params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
- params[ac].txop, params[ac].uapsd,
- ifmgd->tx_tspec[ac].downgraded);
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
link->tx_conf[ac] = params[ac];
- if (!ifmgd->tx_tspec[ac].downgraded &&
- drv_conf_tx(local, link, ac, &params[ac]))
- link_err(link,
- "failed to set TX queue parameters for AC %d\n",
- ac);
- }
+
+ ieee80211_mgd_set_link_qos_params(link);
/* enable WMM or activate new settings */
link->conf->qos = true;
@@ -3904,6 +3928,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;
@@ -4032,11 +4057,11 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
goto out;
}
- sband = ieee80211_get_link_sband(link);
- if (!sband) {
+ if (WARN_ON(!link->conf->chandef.chan)) {
ret = false;
goto out;
}
+ sband = local->hw.wiphy->bands[link->conf->chandef.chan->band];
if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
(!elems->he_cap || !elems->he_operation)) {
@@ -4571,6 +4596,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;
@@ -4580,7 +4610,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;
@@ -4786,6 +4818,40 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
return ret;
}
+static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,
+ u8 *dtim_count, u8 *dtim_period)
+{
+ const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len);
+ const u8 *idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, ies->data,
+ ies->len);
+ const struct ieee80211_tim_ie *tim = NULL;
+ const struct ieee80211_bssid_index *idx;
+ bool valid = tim_ie && tim_ie[1] >= 2;
+
+ if (valid)
+ tim = (void *)(tim_ie + 2);
+
+ if (dtim_count)
+ *dtim_count = valid ? tim->dtim_count : 0;
+
+ if (dtim_period)
+ *dtim_period = valid ? tim->dtim_period : 0;
+
+ /* Check if value is overridden by non-transmitted profile */
+ if (!idx_ie || idx_ie[1] < 3)
+ return valid;
+
+ idx = (void *)(idx_ie + 2);
+
+ if (dtim_count)
+ *dtim_count = idx->dtim_count;
+
+ if (dtim_period)
+ *dtim_period = idx->dtim_period;
+
+ return true;
+}
+
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
struct ieee802_11_elems *elems,
@@ -4849,11 +4915,24 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
goto out_err;
if (link_id != assoc_data->assoc_link_id) {
- err = ieee80211_prep_channel(sdata, link,
- assoc_data->link[link_id].bss,
+ struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
+ const struct cfg80211_bss_ies *ies;
+
+ rcu_read_lock();
+ ies = rcu_dereference(cbss->ies);
+ ieee80211_get_dtim(ies,
+ &link->conf->sync_dtim_count,
+ &link->u.mgd.dtim_period);
+ link->conf->dtim_period = link->u.mgd.dtim_period ?: 1;
+ link->conf->beacon_int = cbss->beacon_interval;
+ rcu_read_unlock();
+
+ err = ieee80211_prep_channel(sdata, link, cbss,
&link->u.mgd.conn_flags);
- if (err)
+ if (err) {
+ link_info(link, "prep_channel failed\n");
goto out_err;
+ }
}
err = ieee80211_mgd_setup_link_sta(link, sta, link_sta,
@@ -4935,6 +5014,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;
@@ -4989,7 +5073,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;
@@ -5122,7 +5208,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);
@@ -5354,6 +5440,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);
@@ -5372,6 +5462,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) {
@@ -5390,8 +5483,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;
@@ -5457,9 +5550,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;
@@ -5673,6 +5767,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,
@@ -5749,6 +5850,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
}
break;
}
+out:
sdata_unlock(sdata);
}
@@ -6284,6 +6386,8 @@ void ieee80211_mgd_setup_link(struct ieee80211_link_data *link)
if (sdata->u.mgd.assoc_data)
ether_addr_copy(link->conf->addr,
sdata->u.mgd.assoc_data->link[link_id].addr);
+ else if (!is_valid_ether_addr(link->conf->addr))
+ eth_random_addr(link->conf->addr);
}
/* scan finished notification */
@@ -6300,40 +6404,6 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
rcu_read_unlock();
}
-static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,
- u8 *dtim_count, u8 *dtim_period)
-{
- const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len);
- const u8 *idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, ies->data,
- ies->len);
- const struct ieee80211_tim_ie *tim = NULL;
- const struct ieee80211_bssid_index *idx;
- bool valid = tim_ie && tim_ie[1] >= 2;
-
- if (valid)
- tim = (void *)(tim_ie + 2);
-
- if (dtim_count)
- *dtim_count = valid ? tim->dtim_count : 0;
-
- if (dtim_period)
- *dtim_period = valid ? tim->dtim_period : 0;
-
- /* Check if value is overridden by non-transmitted profile */
- if (!idx_ie || idx_ie[1] < 3)
- return valid;
-
- idx = (void *)(idx_ie + 2);
-
- if (dtim_count)
- *dtim_count = idx->dtim_count;
-
- if (dtim_period)
- *dtim_period = idx->dtim_period;
-
- return true;
-}
-
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss, s8 link_id,
const u8 *ap_mld_addr, bool assoc,
@@ -6371,9 +6441,6 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
goto out_err;
}
- if (mlo && !is_valid_ether_addr(link->conf->addr))
- eth_random_addr(link->conf->addr);
-
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) {
err = -EINVAL;
goto out_err;
@@ -6839,22 +6906,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
size += req->links[i].elems_len;
- if (req->ap_mld_addr) {
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- if (!req->links[i].bss)
- continue;
- if (i == assoc_link_id)
- continue;
- /*
- * For now, support only a single link in MLO, we
- * don't have the necessary parsing of the multi-
- * link element in the association response, etc.
- */
- sdata_info(sdata,
- "refusing MLO association with >1 links\n");
- return -EINVAL;
- }
- }
+ /* FIXME: no support for 4-addr MLO yet */
+ if (sdata->u.mgd.use_4addr && req->link_id >= 0)
+ return -EOPNOTSUPP;
assoc_data = kzalloc(size, GFP_KERNEL);
if (!assoc_data)
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 788a82f9c74d..7f3f5f51081d 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2019-2021 Intel Corporation
+ * Copyright (C) 2019-2022 Intel Corporation
*/
#include <linux/netdevice.h>
#include <linux/types.h>
@@ -1479,7 +1479,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
* - for fallback rates, to increase chances of getting through
*/
if (offset > 0 ||
- (mi->sta->smps_mode == IEEE80211_SMPS_DYNAMIC &&
+ (mi->sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC &&
group->streams > 1)) {
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
flags |= IEEE80211_TX_RC_USE_RTS_CTS;
@@ -1570,7 +1570,8 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (i < IEEE80211_TX_RATE_TABLE_SIZE)
rates->rate[i].idx = -1;
- mi->sta->max_rc_amsdu_len = minstrel_ht_get_max_amsdu_len(mi);
+ mi->sta->deflink.agg.max_rc_amsdu_len = minstrel_ht_get_max_amsdu_len(mi);
+ ieee80211_sta_recalc_aggregates(mi->sta);
rate_control_set_rates(mp->hw, mi->sta, rates);
}
@@ -1781,7 +1782,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
nss = minstrel_mcs_groups[i].streams;
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
- if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
+ if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
continue;
/* HT rate */
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 45d7e71661e3..bd215fe3c796 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -49,7 +49,7 @@ static struct sk_buff *ieee80211_clean_skb(struct sk_buff *skb,
if (present_fcs_len)
__pskb_trim(skb, skb->len - present_fcs_len);
- __pskb_pull(skb, rtap_space);
+ pskb_pull(skb, rtap_space);
hdr = (void *)skb->data;
fc = hdr->frame_control;
@@ -74,7 +74,7 @@ static struct sk_buff *ieee80211_clean_skb(struct sk_buff *skb,
memmove(skb->data + IEEE80211_HT_CTL_LEN, skb->data,
hdrlen - IEEE80211_HT_CTL_LEN);
- __pskb_pull(skb, IEEE80211_HT_CTL_LEN);
+ pskb_pull(skb, IEEE80211_HT_CTL_LEN);
return skb;
}
@@ -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;
}
@@ -1441,7 +1452,7 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) {
I802_DEBUG_INC(rx->local->dot11FrameDuplicateCount);
- rx->sta->deflink.rx_stats.num_duplicates++;
+ rx->link_sta->rx_stats.num_duplicates++;
return RX_DROP_UNUSABLE;
} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
@@ -1720,12 +1731,13 @@ static ieee80211_rx_result debug_noinline
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
struct sta_info *sta = rx->sta;
+ struct link_sta_info *link_sta = rx->link_sta;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
int i;
- if (!sta)
+ if (!sta || !link_sta)
return RX_CONTINUE;
/*
@@ -1741,47 +1753,47 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
NL80211_IFTYPE_ADHOC);
if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&
test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
- sta->deflink.rx_stats.last_rx = jiffies;
+ link_sta->rx_stats.last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control) &&
!is_multicast_ether_addr(hdr->addr1))
- sta->deflink.rx_stats.last_rate =
+ link_sta->rx_stats.last_rate =
sta_stats_encode_rate(status);
}
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
- sta->deflink.rx_stats.last_rx = jiffies;
+ link_sta->rx_stats.last_rx = jiffies;
} else if (!ieee80211_is_s1g_beacon(hdr->frame_control) &&
!is_multicast_ether_addr(hdr->addr1)) {
/*
* Mesh beacons will update last_rx when if they are found to
* match the current local configuration when processed.
*/
- sta->deflink.rx_stats.last_rx = jiffies;
+ link_sta->rx_stats.last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control))
- sta->deflink.rx_stats.last_rate = sta_stats_encode_rate(status);
+ link_sta->rx_stats.last_rate = sta_stats_encode_rate(status);
}
- sta->deflink.rx_stats.fragments++;
+ link_sta->rx_stats.fragments++;
- u64_stats_update_begin(&rx->sta->deflink.rx_stats.syncp);
- sta->deflink.rx_stats.bytes += rx->skb->len;
- u64_stats_update_end(&rx->sta->deflink.rx_stats.syncp);
+ u64_stats_update_begin(&link_sta->rx_stats.syncp);
+ link_sta->rx_stats.bytes += rx->skb->len;
+ u64_stats_update_end(&link_sta->rx_stats.syncp);
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
- sta->deflink.rx_stats.last_signal = status->signal;
- ewma_signal_add(&sta->deflink.rx_stats_avg.signal,
+ link_sta->rx_stats.last_signal = status->signal;
+ ewma_signal_add(&link_sta->rx_stats_avg.signal,
-status->signal);
}
if (status->chains) {
- sta->deflink.rx_stats.chains = status->chains;
+ link_sta->rx_stats.chains = status->chains;
for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
int signal = status->chain_signal[i];
if (!(status->chains & BIT(i)))
continue;
- sta->deflink.rx_stats.chain_signal_last[i] = signal;
- ewma_signal_add(&sta->deflink.rx_stats_avg.chain_signal[i],
+ link_sta->rx_stats.chain_signal_last[i] = signal;
+ ewma_signal_add(&link_sta->rx_stats_avg.chain_signal[i],
-signal);
}
}
@@ -1842,7 +1854,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
* Update counter and free packet here to avoid
* counting this as a dropped packed.
*/
- sta->deflink.rx_stats.packets++;
+ link_sta->rx_stats.packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
@@ -1854,7 +1866,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 +1884,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 +1997,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 +2014,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 +2060,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]);
@@ -2380,7 +2390,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
out:
ieee80211_led_rx(rx->local);
if (rx->sta)
- rx->sta->deflink.rx_stats.packets++;
+ rx->link_sta->rx_stats.packets++;
return RX_CONTINUE;
}
@@ -2656,9 +2666,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
* for non-QoS-data frames. Here we know it's a data
* frame, so count MSDUs.
*/
- u64_stats_update_begin(&rx->sta->deflink.rx_stats.syncp);
- rx->sta->deflink.rx_stats.msdu[rx->seqno_idx]++;
- u64_stats_update_end(&rx->sta->deflink.rx_stats.syncp);
+ u64_stats_update_begin(&rx->link_sta->rx_stats.syncp);
+ rx->link_sta->rx_stats.msdu[rx->seqno_idx]++;
+ u64_stats_update_end(&rx->link_sta->rx_stats.syncp);
}
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
@@ -3046,7 +3056,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;
}
}
@@ -3354,7 +3365,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_HT:
/* reject HT action frames from stations not supporting HT */
- if (!rx->sta->sta.deflink.ht_cap.ht_supported)
+ if (!rx->link_sta->pub->ht_cap.ht_supported)
goto invalid;
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
@@ -3394,9 +3405,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
}
/* if no change do nothing */
- if (rx->sta->sta.smps_mode == smps_mode)
+ if (rx->link_sta->pub->smps_mode == smps_mode)
goto handled;
- rx->sta->sta.smps_mode = smps_mode;
+ rx->link_sta->pub->smps_mode = smps_mode;
sta_opmode.smps_mode =
ieee80211_smps_mode_to_smps_mode(smps_mode);
sta_opmode.changed = STA_OPMODE_SMPS_MODE_CHANGED;
@@ -3418,26 +3429,26 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
struct sta_opmode_info sta_opmode = {};
/* If it doesn't support 40 MHz it can't change ... */
- if (!(rx->sta->sta.deflink.ht_cap.cap &
+ if (!(rx->link_sta->pub->ht_cap.cap &
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
goto handled;
if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
max_bw = IEEE80211_STA_RX_BW_20;
else
- max_bw = ieee80211_sta_cap_rx_bw(&rx->sta->deflink);
+ max_bw = ieee80211_sta_cap_rx_bw(rx->link_sta);
/* set cur_max_bandwidth and recalc sta bw */
- rx->sta->deflink.cur_max_bandwidth = max_bw;
- new_bw = ieee80211_sta_cur_vht_bw(&rx->sta->deflink);
+ rx->link_sta->cur_max_bandwidth = max_bw;
+ new_bw = ieee80211_sta_cur_vht_bw(rx->link_sta);
- if (rx->sta->sta.deflink.bandwidth == new_bw)
+ if (rx->link_sta->pub->bandwidth == new_bw)
goto handled;
- rx->sta->sta.deflink.bandwidth = new_bw;
+ rx->link_sta->pub->bandwidth = new_bw;
sband = rx->local->hw.wiphy->bands[status->band];
sta_opmode.bw =
- ieee80211_sta_rx_bw_to_chan_width(&rx->sta->deflink);
+ ieee80211_sta_rx_bw_to_chan_width(rx->link_sta);
sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED;
rate_control_rate_update(local, sband, rx->sta, 0,
@@ -3631,12 +3642,12 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
handled:
if (rx->sta)
- rx->sta->deflink.rx_stats.packets++;
+ rx->link_sta->rx_stats.packets++;
dev_kfree_skb(rx->skb);
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;
}
@@ -3675,7 +3686,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
if (cfg80211_rx_mgmt_ext(&rx->sdata->wdev, &info)) {
if (rx->sta)
- rx->sta->deflink.rx_stats.packets++;
+ rx->link_sta->rx_stats.packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
@@ -3713,7 +3724,7 @@ ieee80211_rx_h_action_post_userspace(struct ieee80211_rx_data *rx)
handled:
if (rx->sta)
- rx->sta->deflink.rx_stats.packets++;
+ rx->link_sta->rx_stats.packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
@@ -3794,7 +3805,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 +3862,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;
}
@@ -3933,7 +3944,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
case RX_DROP_MONITOR:
I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop);
if (rx->sta)
- rx->sta->deflink.rx_stats.dropped++;
+ rx->link_sta->rx_stats.dropped++;
fallthrough;
case RX_CONTINUE: {
struct ieee80211_rate *rate = NULL;
@@ -3952,7 +3963,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
case RX_DROP_UNUSABLE:
I802_DEBUG_INC(rx->sdata->local->rx_handlers_drop);
if (rx->sta)
- rx->sta->deflink.rx_stats.dropped++;
+ rx->link_sta->rx_stats.dropped++;
dev_kfree_skb(rx->skb);
break;
case RX_QUEUED:
@@ -4097,6 +4108,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
/* FIXME: statistics won't be right with this */
link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
rx.link = rcu_dereference(sta->sdata->link[link_id]);
+ rx.link_sta = rcu_dereference(sta->link[link_id]);
ieee80211_rx_handlers(&rx, &frames);
}
@@ -4512,6 +4524,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)
@@ -4519,19 +4540,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);
}
@@ -4547,7 +4579,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);
}
}
@@ -4623,7 +4655,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
@@ -4724,8 +4757,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;
@@ -4773,7 +4817,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;
}
@@ -4831,6 +4885,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;
@@ -4851,7 +4906,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)
@@ -4881,7 +4960,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);
@@ -4897,6 +4988,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;
@@ -4941,10 +5033,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;
@@ -4958,6 +5079,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);
@@ -4966,6 +5094,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;
@@ -5108,6 +5243,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/scan.c b/net/mac80211/scan.c
index c4f2aeb31da3..0e8c4f48c36d 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -485,7 +485,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
/* Set power back to normal operating levels. */
ieee80211_hw_config(local, 0);
- if (!hw_scan) {
+ if (!hw_scan && was_scanning) {
ieee80211_configure_filter(local);
drv_sw_scan_complete(local, scan_sdata);
ieee80211_offchannel_return(local);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 58998d821778..cebfd148bb40 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -274,6 +274,43 @@ link_sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr)
return NULL;
}
+struct ieee80211_sta *
+ieee80211_find_sta_by_link_addrs(struct ieee80211_hw *hw,
+ const u8 *addr,
+ const u8 *localaddr,
+ unsigned int *link_id)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct link_sta_info *link_sta;
+ struct rhlist_head *tmp;
+
+ for_each_link_sta_info(local, addr, link_sta, tmp) {
+ struct sta_info *sta = link_sta->sta;
+ struct ieee80211_link_data *link;
+ u8 _link_id = link_sta->link_id;
+
+ if (!localaddr) {
+ if (link_id)
+ *link_id = _link_id;
+ return &sta->sta;
+ }
+
+ link = rcu_dereference(sta->sdata->link[_link_id]);
+ if (!link)
+ continue;
+
+ if (memcmp(link->conf->addr, localaddr, ETH_ALEN))
+ continue;
+
+ if (link_id)
+ *link_id = _link_id;
+ return &sta->sta;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_link_addrs);
+
struct sta_info *sta_info_get_by_addrs(struct ieee80211_local *local,
const u8 *sta_addr, const u8 *vif_addr)
{
@@ -339,6 +376,8 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id,
sta_info_free_link(&alloc->info);
kfree_rcu(alloc, rcu_head);
}
+
+ ieee80211_sta_recalc_aggregates(&sta->sta);
}
/**
@@ -472,8 +511,12 @@ 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);
+
+ link_sta->smps_mode = IEEE80211_SMPS_OFF;
+ link_sta->agg.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
}
static struct sta_info *
@@ -504,6 +547,8 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta_info_add_link(sta, 0, &sta->deflink, &sta->sta.deflink);
}
+ sta->sta.cur = &sta->sta.deflink.agg;
+
spin_lock_init(&sta->lock);
spin_lock_init(&sta->ps_lock);
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
@@ -627,9 +672,6 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata,
}
}
- sta->sta.smps_mode = IEEE80211_SMPS_OFF;
- sta->sta.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
-
sta->cparams.ce_threshold = CODEL_DISABLED_THRESHOLD;
sta->cparams.target = MS2TIME(20);
sta->cparams.interval = MS2TIME(100);
@@ -2085,6 +2127,44 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
}
EXPORT_SYMBOL(ieee80211_sta_register_airtime);
+void ieee80211_sta_recalc_aggregates(struct ieee80211_sta *pubsta)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_link_sta *link_sta;
+ int link_id, i;
+ bool first = true;
+
+ if (!pubsta->valid_links || !pubsta->mlo) {
+ pubsta->cur = &pubsta->deflink.agg;
+ return;
+ }
+
+ rcu_read_lock();
+ for_each_sta_active_link(&sta->sdata->vif, pubsta, link_sta, link_id) {
+ if (first) {
+ sta->cur = pubsta->deflink.agg;
+ first = false;
+ continue;
+ }
+
+ sta->cur.max_amsdu_len =
+ min(sta->cur.max_amsdu_len,
+ link_sta->agg.max_amsdu_len);
+ sta->cur.max_rc_amsdu_len =
+ min(sta->cur.max_rc_amsdu_len,
+ link_sta->agg.max_rc_amsdu_len);
+
+ for (i = 0; i < ARRAY_SIZE(sta->cur.max_tid_amsdu_len); i++)
+ sta->cur.max_tid_amsdu_len[i] =
+ min(sta->cur.max_tid_amsdu_len[i],
+ link_sta->agg.max_tid_amsdu_len[i]);
+ }
+ rcu_read_unlock();
+
+ pubsta->cur = &sta->cur;
+}
+EXPORT_SYMBOL(ieee80211_sta_recalc_aggregates);
+
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
struct sta_info *sta, u8 ac,
u16 tx_airtime, bool tx_completed)
@@ -2777,10 +2857,13 @@ 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;
- }
+
+ /* Ensure the values are updated for the driver,
+ * redone by sta_remove_link on failure.
+ */
+ ieee80211_sta_recalc_aggregates(&sta->sta);
ret = drv_change_sta_links(sdata->local, sdata, &sta->sta,
old_links, new_links);
@@ -2799,6 +2882,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 +2890,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);
}
@@ -2834,3 +2917,13 @@ void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta,
if (val)
sta->sta.max_amsdu_subframes = 4 << val;
}
+
+#ifdef CONFIG_LOCKDEP
+bool lockdep_sta_mutex_held(struct ieee80211_sta *pubsta)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+
+ return lockdep_is_held(&sta->local->sta_mtx);
+}
+EXPORT_SYMBOL(lockdep_sta_mutex_held);
+#endif
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 2eb3a9452e07..2517ea714dc4 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -622,6 +622,8 @@ struct link_sta_info {
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
* the BSS one.
* @frags: fragment cache
+ * @cur: storage for aggregation data
+ * &struct ieee80211_sta points either here or to deflink.agg.
* @deflink: This is the default link STA information, for non MLO STA all link
* specific STA information is accessed through @deflink or through
* link[0] which points to address of @deflink. For MLO Link STA
@@ -705,6 +707,7 @@ struct sta_info {
struct ieee80211_fragment_cache frags;
+ struct ieee80211_sta_aggregates cur;
struct link_sta_info deflink;
struct link_sta_info __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 13249e97a069..27c964be102e 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;
@@ -3350,7 +3387,7 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
int subframe_len = skb->len - ETH_ALEN;
u8 max_subframes = sta->sta.max_amsdu_subframes;
int max_frags = local->hw.max_tx_fragments;
- int max_amsdu_len = sta->sta.max_amsdu_len;
+ int max_amsdu_len = sta->sta.cur->max_amsdu_len;
int orig_truesize;
u32 flow_idx;
__be16 len;
@@ -3376,13 +3413,13 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
return false;
- if (sta->sta.max_rc_amsdu_len)
+ if (sta->sta.cur->max_rc_amsdu_len)
max_amsdu_len = min_t(int, max_amsdu_len,
- sta->sta.max_rc_amsdu_len);
+ sta->sta.cur->max_rc_amsdu_len);
- if (sta->sta.max_tid_amsdu_len[tid])
+ if (sta->sta.cur->max_tid_amsdu_len[tid])
max_amsdu_len = min_t(int, max_amsdu_len,
- sta->sta.max_tid_amsdu_len[tid]);
+ sta->sta.cur->max_tid_amsdu_len[tid]);
flow_idx = fq_flow_idx(fq, skb);
@@ -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;
@@ -5430,33 +5469,39 @@ EXPORT_SYMBOL(ieee80211_pspoll_get);
struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- bool qos_ok)
+ int link_id, bool qos_ok)
{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_link_data *link = NULL;
struct ieee80211_hdr_3addr *nullfunc;
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_local *local;
struct sk_buff *skb;
bool qos = false;
if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
return NULL;
- sdata = vif_to_sdata(vif);
- local = sdata->local;
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ sizeof(*nullfunc) + 2);
+ if (!skb)
+ return NULL;
+ rcu_read_lock();
if (qos_ok) {
struct sta_info *sta;
- rcu_read_lock();
- sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
+ sta = sta_info_get(sdata, vif->cfg.ap_addr);
qos = sta && sta->sta.wme;
- rcu_read_unlock();
}
- skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*nullfunc) + 2);
- if (!skb)
- return NULL;
+ if (link_id >= 0) {
+ link = rcu_dereference(sdata->link[link_id]);
+ if (WARN_ON_ONCE(!link)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return NULL;
+ }
+ }
skb_reserve(skb, local->hw.extra_tx_headroom);
@@ -5477,9 +5522,16 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
skb_put_data(skb, &qoshdr, sizeof(qoshdr));
}
- memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
- memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
- memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ if (link) {
+ memcpy(nullfunc->addr1, link->conf->bssid, ETH_ALEN);
+ memcpy(nullfunc->addr2, link->conf->addr, ETH_ALEN);
+ memcpy(nullfunc->addr3, link->conf->bssid, ETH_ALEN);
+ } else {
+ memcpy(nullfunc->addr1, vif->cfg.ap_addr, ETH_ALEN);
+ memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
+ memcpy(nullfunc->addr3, vif->cfg.ap_addr, ETH_ALEN);
+ }
+ rcu_read_unlock();
return skb;
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index efcefb2dd882..bf7461c41bef 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;
}
@@ -2526,7 +2530,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (link)
ieee80211_assign_chanctx(local, sdata, link);
}
- sdata_unlock(sdata);
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
@@ -2545,6 +2548,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
&sdata->deflink.tx_conf[i]);
break;
}
+ sdata_unlock(sdata);
/* common change flags for all interface types */
changed = BSS_CHANGED_ERP_CTS_PROT |
@@ -2653,23 +2657,21 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
/* APs are now beaconing, add back stations */
- mutex_lock(&local->sta_mtx);
- list_for_each_entry(sta, &local->sta_list, list) {
- enum ieee80211_sta_state state;
-
- if (!sta->uploaded)
- continue;
-
- if (sta->sdata->vif.type != NL80211_IFTYPE_AP &&
- sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata))
continue;
- for (state = IEEE80211_STA_NOTEXIST;
- state < sta->sta_state; state++)
- WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
- state + 1));
+ sdata_lock(sdata);
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_AP:
+ ieee80211_reconfig_stations(sdata);
+ break;
+ default:
+ break;
+ }
+ sdata_unlock(sdata);
}
- mutex_unlock(&local->sta_mtx);
/* add back keys */
list_for_each_entry(sdata, &local->interfaces, list)
@@ -2898,7 +2900,7 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata,
*/
rcu_read_unlock();
- if (WARN_ON_ONCE(!chanctx_conf))
+ if (!chanctx_conf)
goto unlock;
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx,
@@ -3080,6 +3082,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 +3109,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);
@@ -3507,8 +3510,7 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
return true;
}
-void ieee80211_chandef_eht_oper(struct ieee80211_sub_if_data *sdata,
- const struct ieee80211_eht_operation *eht_oper,
+void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper,
bool support_160, bool support_320,
struct cfg80211_chan_def *chandef)
{
@@ -3684,7 +3686,7 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
support_320 =
eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
- ieee80211_chandef_eht_oper(sdata, eht_oper, support_160,
+ ieee80211_chandef_eht_oper(eht_oper, support_160,
support_320, &he_chandef);
}
@@ -4770,6 +4772,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 +4784,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 +4800,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 +4812,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/mac80211/vht.c b/net/mac80211/vht.c
index b2b09d421e8b..803de5881485 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -323,16 +323,18 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
*/
switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
- link_sta->sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
break;
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
- link_sta->sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
break;
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
default:
- link_sta->sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
+ link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
break;
}
+
+ ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
}
/* FIXME: move this to some better location - parses HE/EHT now */