mac80211: fix bufferable MMPDU RX handling
authorJohannes Berg <johannes.berg@intel.com>
Fri, 24 Jan 2014 13:41:44 +0000 (14:41 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 4 Feb 2014 20:58:15 +0000 (21:58 +0100)
Action, disassoc and deauth frames are bufferable, and as such don't
have the PM bit in the frame control field reserved which means we
need to react to the bit when receiving in such a frame.

Fix this by introducing a new helper ieee80211_is_bufferable_mmpdu()
and using it for the RX path that currently ignores the PM bit in
any non-data frames for doze->wake transitions, but listens to it in
all frames for wake->doze transitions, both of which are wrong.

Also use the new helper in the TX path to clean up the code.

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

index 923c478030a3c245aa2c186fd7f0955ce1a2401b..1e3912d1b029406def00c69a74ca657e222cd7b6 100644 (file)
@@ -596,6 +596,20 @@ static inline int ieee80211_is_qos_nullfunc(__le16 fc)
               cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
 }
 
+/**
+ * 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
index 3b7a750ebc708e3eb1f534d59797e6a0f49c446d..79a89fe9d616dd947b9cd154091df7342a41ba22 100644 (file)
@@ -1311,18 +1311,15 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
            !ieee80211_has_morefrags(hdr->frame_control) &&
            !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
            (rx->sdata->vif.type == NL80211_IFTYPE_AP ||
-            rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
+            rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           /* PM bit is only checked in frames where it isn't reserved,
+            * in AP mode it's reserved in non-bufferable management frames
+            * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field)
+            */
+           (!ieee80211_is_mgmt(hdr->frame_control) ||
+            ieee80211_is_bufferable_mmpdu(hdr->frame_control))) {
                if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-                       /*
-                        * Ignore doze->wake transitions that are
-                        * indicated by non-data frames, the standard
-                        * is unclear here, but for example going to
-                        * PS mode and then scanning would cause a
-                        * doze->wake transition for the probe request,
-                        * and that is clearly undesirable.
-                        */
-                       if (ieee80211_is_data(hdr->frame_control) &&
-                           !ieee80211_has_pm(hdr->frame_control))
+                       if (!ieee80211_has_pm(hdr->frame_control))
                                sta_ps_end(sta);
                } else {
                        if (ieee80211_has_pm(hdr->frame_control))
index 07a7f38dc348a420e97ba74c7493e4eb4a4c28fc..5476a69b45c9244123dd21dbd60882b3f98b6e70 100644 (file)
@@ -522,11 +522,8 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
        if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
                return TX_CONTINUE;
 
-       /* only deauth, disassoc and action are bufferable MMPDUs */
        if (ieee80211_is_mgmt(hdr->frame_control) &&
-           !ieee80211_is_deauth(hdr->frame_control) &&
-           !ieee80211_is_disassoc(hdr->frame_control) &&
-           !ieee80211_is_action(hdr->frame_control)) {
+           !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
                if (tx->flags & IEEE80211_TX_UNICAST)
                        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
                return TX_CONTINUE;