wifi: ieee80211: correctly mark FTM frames non-bufferable
authorJohannes Berg <johannes.berg@intel.com>
Fri, 31 Mar 2023 14:59:08 +0000 (16:59 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 13 Apr 2023 14:32:02 +0000 (16:32 +0200)
The checks of whether or not a frame is bufferable were not
taking into account that some action frames aren't, such as
FTM. Check this, which requires some changes to the function
ieee80211_is_bufferable_mmpdu() since we need the whole skb
for the checks now.

Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
drivers/net/wireless/mediatek/mt76/tx.c
include/linux/ieee80211.h
net/mac80211/tx.c

index 51f21cb..478442e 100644 (file)
@@ -604,8 +604,9 @@ static void iwl_mvm_skb_prepare_status(struct sk_buff *skb,
 static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
                                      struct iwl_mvm_vif_link_info *link,
                                      struct ieee80211_tx_info *info,
-                                     struct ieee80211_hdr *hdr)
+                                     struct sk_buff *skb)
 {
+       struct ieee80211_hdr *hdr = (void *)skb->data;
        __le16 fc = hdr->frame_control;
 
        switch (info->control.vif->type) {
@@ -622,7 +623,7 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
                 * reason 7 ("Class 3 frame received from nonassociated STA").
                 */
                if (ieee80211_is_mgmt(fc) &&
-                   (!ieee80211_is_bufferable_mmpdu(fc) ||
+                   (!ieee80211_is_bufferable_mmpdu(skb) ||
                     ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc)))
                        return link->mgmt_queue;
 
@@ -755,7 +756,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
                                sta_id = link->mcast_sta.sta_id;
 
                        queue = iwl_mvm_get_ctrl_vif_queue(mvm, link, &info,
-                                                          hdr);
+                                                          skb);
                } else if (info.control.vif->type == NL80211_IFTYPE_MONITOR) {
                        queue = mvm->snif_queue;
                        sta_id = mvm->snif_sta.sta_id;
index 1f309d0..3ad9742 100644 (file)
@@ -330,7 +330,7 @@ mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta,
        if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) &&
            !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
            !ieee80211_is_data(hdr->frame_control) &&
-           !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
+           !ieee80211_is_bufferable_mmpdu(skb)) {
                qid = MT_TXQ_PSD;
        }
 
index 0583b2b..c4cf296 100644 (file)
@@ -783,20 +783,6 @@ static inline bool ieee80211_is_any_nullfunc(__le16 fc)
 }
 
 /**
- * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
- * @fc: frame control field in little-endian byteorder
- */
-static inline bool ieee80211_is_bufferable_mmpdu(__le16 fc)
-{
-       /* IEEE 802.11-2012, definition of "bufferable management frame";
-        * note that this ignores the IBSS special case. */
-       return ieee80211_is_mgmt(fc) &&
-              (ieee80211_is_action(fc) ||
-               ieee80211_is_disassoc(fc) ||
-               ieee80211_is_deauth(fc));
-}
-
-/**
  * ieee80211_is_first_frag - check if IEEE80211_SCTL_FRAG is not set
  * @seq_ctrl: frame sequence control bytes in little-endian byteorder
  */
@@ -4133,6 +4119,44 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
 }
 
 /**
+ * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU
+ * @skb: the skb to check, starting with the 802.11 header
+ */
+static inline bool ieee80211_is_bufferable_mmpdu(struct sk_buff *skb)
+{
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
+       __le16 fc = mgmt->frame_control;
+
+       /*
+        * IEEE 802.11 REVme D2.0 definition of bufferable MMPDU;
+        * note that this ignores the IBSS special case.
+        */
+       if (!ieee80211_is_mgmt(fc))
+               return false;
+
+       if (ieee80211_is_disassoc(fc) || ieee80211_is_deauth(fc))
+               return true;
+
+       if (!ieee80211_is_action(fc))
+               return false;
+
+       if (skb->len < offsetofend(typeof(*mgmt), u.action.u.ftm.action_code))
+               return true;
+
+       /* action frame - additionally check for non-bufferable FTM */
+
+       if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
+           mgmt->u.action.category != WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION)
+               return true;
+
+       if (mgmt->u.action.u.ftm.action_code == WLAN_PUB_ACTION_FTM_REQUEST ||
+           mgmt->u.action.u.ftm.action_code == WLAN_PUB_ACTION_FTM_RESPONSE)
+               return false;
+
+       return true;
+}
+
+/**
  * _ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame
  * @hdr: the frame (buffer must include at least the first octet of payload)
  */
index dfe6b9c..1a33274 100644 (file)
@@ -488,7 +488,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                int ac = skb_get_queue_mapping(tx->skb);
 
                if (ieee80211_is_mgmt(hdr->frame_control) &&
-                   !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
+                   !ieee80211_is_bufferable_mmpdu(tx->skb)) {
                        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
                        return TX_CONTINUE;
                }
@@ -1323,7 +1323,7 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
        if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
            unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
                if ((!ieee80211_is_mgmt(hdr->frame_control) ||
-                    ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
+                    ieee80211_is_bufferable_mmpdu(skb) ||
                     vif->type == NL80211_IFTYPE_STATION) &&
                    sta && sta->uploaded) {
                        /*