wifi: mac80211: expand ieee80211_mgmt_tx() for MLO
authorJohannes Berg <johannes.berg@intel.com>
Mon, 18 Jul 2022 19:36:08 +0000 (21:36 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 22 Jul 2022 12:28:35 +0000 (14:28 +0200)
There are a couple of new things that should be possible
with MLO:
 * selecting the link to transmit to a station by link ID,
   which a previous patch added to the nl80211 API
 * selecting the link by frequency, similarly
 * allowing transmittion to an MLD without specifying any
   channel or link ID, with MLD addresses

Enable these use cases. Also fix the address comparison
in client mode to use the AP (MLD) address.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/agg-tx.c
net/mac80211/ieee80211_i.h
net/mac80211/offchannel.c
net/mac80211/rx.c
net/mac80211/tx.c

index e9f8be7ba9a693a9d63196d6d8b500691aee04b8..1afd45239fe6155f5903616fcfeb1d612ac9d82f 100644 (file)
@@ -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.
  */
index b13f4b7b740df46f66cc4abafbba5f1b358f2761..07c892aa8c73ff57ec8e5fcc524a3dadae67769f 100644 (file)
@@ -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);
 
index 5fc4392ba5077460a6123c4feac9316cb95c0969..4ec6fb96ba417e9ea77fc4ae93418959e199a5a2 100644 (file)
@@ -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);
 }
 
 /**
index be79ae68754e4c2411e395bd0c33d9b0a6a1eb02..d78c82d6b6966f717329d869750d145f53f8f385 100644 (file)
@@ -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;
        }
index 6cb5989c6ae2a58094d674c309dd3186b1014654..58ff2cc7bcc9e3e621f7f7470edc1a14f3171e16 100644 (file)
@@ -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);
index 06ec152e8188ae47ed18fa7fb3d5a63c7a477745..f24565064f08e5dc86a1318fb4ddcb20dcf18c2d 100644 (file)
@@ -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();
 }