summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/mac80211.h4
-rw-r--r--net/mac80211/agg-tx.c4
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/offchannel.c47
-rw-r--r--net/mac80211/rx.c2
-rw-r--r--net/mac80211/tx.c11
6 files changed, 51 insertions, 25 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e9f8be7ba9a6..1afd45239fe6 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -885,7 +885,9 @@ enum mac80211_tx_info_flags {
* @IEEE80211_TX_CTRL_MLO_LINK: If not @IEEE80211_LINK_UNSPECIFIED, this
* frame should be transmitted on the specific link. This really is
* only relevant for frames that do not have data present, and is
- * also not used for 802.3 format frames.
+ * also not used for 802.3 format frames. Note that even if the frame
+ * is on a specific link, address translation might still apply if
+ * it's intended for an MLD.
*
* These flags are used in tx_info->control.flags.
*/
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index b13f4b7b740d..07c892aa8c73 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -106,7 +106,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.addba_req.start_seq_num =
cpu_to_le16(start_seq_num << 4);
- ieee80211_tx_skb_tid(sdata, skb, tid);
+ ieee80211_tx_skb_tid(sdata, skb, tid, -1);
}
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -135,7 +135,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_REQ_TX_STATUS;
- ieee80211_tx_skb_tid(sdata, skb, tid);
+ ieee80211_tx_skb_tid(sdata, skb, tid, -1);
}
EXPORT_SYMBOL(ieee80211_send_bar);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5fc4392ba507..4ec6fb96ba41 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2141,7 +2141,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid,
+ struct sk_buff *skb, int tid, int link_id,
enum nl80211_band band);
/* sta_out needs to be checked for ERR_PTR() before using */
@@ -2155,18 +2155,18 @@ ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
enum nl80211_band band)
{
rcu_read_lock();
- __ieee80211_tx_skb_tid_band(sdata, skb, tid, band);
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
rcu_read_unlock();
}
void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid);
+ struct sk_buff *skb, int tid, int link_id);
static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
- ieee80211_tx_skb_tid(sdata, skb, 7);
+ ieee80211_tx_skb_tid(sdata, skb, 7, -1);
}
/**
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index be79ae68754e..d78c82d6b696 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -769,9 +769,11 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
- struct sta_info *sta;
+ struct sta_info *sta = NULL;
const struct ieee80211_mgmt *mgmt = (void *)params->buf;
bool need_offchan = false;
+ bool mlo_sta = false;
+ int link_id = -1;
u32 flags;
int ret;
u8 *data;
@@ -804,16 +806,30 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
!ieee80211_vif_is_mesh(&sdata->vif) &&
!sdata->bss->active)
need_offchan = true;
+
+ rcu_read_lock();
+ sta = sta_info_get_bss(sdata, mgmt->da);
+ mlo_sta = sta && sta->sta.mlo;
+
if (!ieee80211_is_action(mgmt->frame_control) ||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
- mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
+ mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
+ rcu_read_unlock();
break;
- rcu_read_lock();
- sta = sta_info_get_bss(sdata, mgmt->da);
- rcu_read_unlock();
- if (!sta)
+ }
+
+ if (!sta) {
+ rcu_read_unlock();
return -ENOLINK;
+ }
+ if (params->link_id >= 0 &&
+ !(sta->sta.valid_links & BIT(params->link_id))) {
+ rcu_read_unlock();
+ return -ENOLINK;
+ }
+ link_id = params->link_id;
+ rcu_read_unlock();
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
@@ -821,8 +837,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
if (!sdata->u.mgd.associated ||
(params->offchan && params->wait &&
local->ops->remain_on_channel &&
- memcmp(sdata->deflink.u.mgd.bssid,
- mgmt->bssid, ETH_ALEN)))
+ memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN)))
need_offchan = true;
sdata_unlock(sdata);
break;
@@ -843,7 +858,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
mutex_lock(&local->mtx);
/* Check if the operating channel is the requested channel */
- if (!need_offchan) {
+ if (!params->chan && mlo_sta) {
+ need_offchan = false;
+ } else if (!need_offchan) {
struct ieee80211_chanctx_conf *chanctx_conf = NULL;
int i;
@@ -860,6 +877,12 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
if (!chanctx_conf)
continue;
+ if (mlo_sta && params->chan == chanctx_conf->def.chan &&
+ ether_addr_equal(sdata->vif.addr, mgmt->sa)) {
+ link_id = i;
+ break;
+ }
+
if (ether_addr_equal(conf->addr, mgmt->sa))
break;
@@ -870,10 +893,6 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
need_offchan = params->chan &&
(params->chan !=
chanctx_conf->def.chan);
- } else if (!params->chan) {
- ret = -EINVAL;
- rcu_read_unlock();
- goto out_unlock;
} else {
need_offchan = true;
}
@@ -943,7 +962,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
}
if (!need_offchan) {
- ieee80211_tx_skb(sdata, skb);
+ ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
ret = 0;
goto out_unlock;
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 6cb5989c6ae2..58ff2cc7bcc9 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3774,7 +3774,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
local->hw.offchannel_tx_hw_queue;
}
- __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7,
+ __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
status->band);
}
dev_kfree_skb(rx->skb);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 06ec152e8188..f24565064f08 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -5647,7 +5647,7 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
EXPORT_SYMBOL(ieee80211_unreserve_tid);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid,
+ struct sk_buff *skb, int tid, int link_id,
enum nl80211_band band)
{
const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -5666,6 +5666,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
if (!sdata->vif.valid_links) {
link = 0;
+ } else if (link_id >= 0) {
+ link = link_id;
} else if (memcmp(sdata->vif.addr, hdr->addr2, ETH_ALEN) == 0) {
/* address from the MLD */
link = IEEE80211_LINK_UNSPECIFIED;
@@ -5702,13 +5704,14 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
}
void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid)
+ struct sk_buff *skb, int tid, int link_id)
{
struct ieee80211_chanctx_conf *chanctx_conf;
enum nl80211_band band;
rcu_read_lock();
if (!sdata->vif.valid_links) {
+ WARN_ON(link_id >= 0);
chanctx_conf =
rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
@@ -5718,11 +5721,13 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
}
band = chanctx_conf->def.chan->band;
} else {
+ WARN_ON(link_id >= 0 &&
+ !(sdata->vif.valid_links & BIT(link_id)));
/* MLD transmissions must not rely on the band */
band = 0;
}
- __ieee80211_tx_skb_tid_band(sdata, skb, tid, band);
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
rcu_read_unlock();
}