WLAN_CATEGORY_QOS = 1,
        WLAN_CATEGORY_DLS = 2,
        WLAN_CATEGORY_BACK = 3,
+       WLAN_CATEGORY_PUBLIC = 4,
        WLAN_CATEGORY_WMM = 17,
 };
 
                return hdr->addr1;
 }
 
+/**
+ * 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)
+ */
+static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
+{
+       if (ieee80211_is_disassoc(hdr->frame_control) ||
+           ieee80211_is_deauth(hdr->frame_control))
+               return true;
+
+       if (ieee80211_is_action(hdr->frame_control)) {
+               u8 *category;
+
+               /*
+                * Action frames, excluding Public Action frames, are Robust
+                * Management Frames. However, if we are looking at a Protected
+                * frame, skip the check since the data may be encrypted and
+                * the frame has already been found to be a Robust Management
+                * Frame (by the other end).
+                */
+               if (ieee80211_has_protected(hdr->frame_control))
+                       return true;
+               category = ((u8 *) hdr) + 24;
+               return *category != WLAN_CATEGORY_PUBLIC;
+       }
+
+       return false;
+}
+
 /**
  * ieee80211_fhss_chan_to_freq - get channel frequency
  * @channel: the FHSS channel
 
        return TX_CONTINUE;
 }
 
+static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,
+                            struct sk_buff *skb)
+{
+       if (!ieee80211_is_mgmt(fc))
+               return 0;
+
+       if (sta == NULL || !test_sta_flags(sta, WLAN_STA_MFP))
+               return 0;
+
+       if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *)
+                                           skb->data))
+               return 0;
+
+       return 1;
+}
+
 static ieee80211_tx_result
 ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
 {
                        if (ieee80211_is_auth(hdr->frame_control))
                                break;
                case ALG_TKIP:
-               case ALG_CCMP:
                        if (!ieee80211_is_data_present(hdr->frame_control))
                                tx->key = NULL;
                        break;
+               case ALG_CCMP:
+                       if (!ieee80211_is_data_present(hdr->frame_control) &&
+                           !ieee80211_use_mfp(hdr->frame_control, tx->sta,
+                                              tx->skb))
+                               tx->key = NULL;
+                       break;
                }
        }
 
 
                                int encrypted)
 {
        __le16 mask_fc;
-       int a4_included;
+       int a4_included, mgmt;
        u8 qos_tid;
        u8 *b_0, *aad;
        u16 data_len, len_a;
        aad = scratch + 4 * AES_BLOCK_LEN;
 
        /*
-        * Mask FC: zero subtype b4 b5 b6
+        * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
         * Retry, PwrMgt, MoreData; set Protected
         */
+       mgmt = ieee80211_is_mgmt(hdr->frame_control);
        mask_fc = hdr->frame_control;
-       mask_fc &= ~cpu_to_le16(0x0070 | IEEE80211_FCTL_RETRY |
+       mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |
                                IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
+       if (!mgmt)
+               mask_fc &= ~cpu_to_le16(0x0070);
        mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
        /* First block, b_0 */
        b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
-       /* Nonce: QoS Priority | A2 | PN */
-       b_0[1] = qos_tid;
+       /* Nonce: Nonce Flags | A2 | PN
+        * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
+        */
+       b_0[1] = qos_tid | (mgmt << 4);
        memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
        memcpy(&b_0[8], pn, CCMP_PN_LEN);
        /* l(m) */
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
-       if (!ieee80211_is_data(hdr->frame_control))
+       if (!ieee80211_is_data(hdr->frame_control) &&
+           !ieee80211_is_robust_mgmt_frame(hdr))
                return RX_CONTINUE;
 
        data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN;