nl80211: Add notification for dropped Deauth/Disassoc
authorJouni Malinen <j@w1.fi>
Wed, 15 Dec 2010 22:52:40 +0000 (00:52 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 16 Dec 2010 20:22:30 +0000 (15:22 -0500)
Add a new notification to indicate that a received, unprotected
Deauthentication or Disassociation frame was dropped due to
management frame protection being in use. This notification is
needed to allow user space (e.g., wpa_supplicant) to implement
SA Query procedure to recover from association state mismatch
between an AP and STA.

This is needed to avoid getting stuck in non-working state when MFP
(IEEE 802.11w) is used and a protected Deauthentication or
Disassociation frame is dropped for any reason. After that, the
station would silently discard any unprotected Deauthentication or
Disassociation frame that could be indicating that the AP does not
have association for the STA (when the Reason Code would be 6 or 7).
IEEE Std 802.11w-2009, 11.13 describes this recovery mechanism.

Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/nl80211.h
include/net/cfg80211.h
net/mac80211/rx.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h

index 1cee56b3a79abf50c02a50a13d982805c7e34ac3..7483a89cee8f5bdd8027d265b85ade3e1af27d0b 100644 (file)
  * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
  *     network is determined by the network interface.
  *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ *     notification. This event is used to indicate that an unprotected
+ *     deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ *     notification. This event is used to indicate that an unprotected
+ *     disassociation frame was dropped when MFP is in use.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -508,6 +515,9 @@ enum nl80211_commands {
        NL80211_CMD_JOIN_MESH,
        NL80211_CMD_LEAVE_MESH,
 
+       NL80211_CMD_UNPROT_DEAUTHENTICATE,
+       NL80211_CMD_UNPROT_DISASSOCIATE,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
index f45e15f124461233a87fb5e1dac0014c5c11992f..3d1c09b777e80f12274677a67ab46146fc5193eb 100644 (file)
@@ -2359,6 +2359,32 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
 void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf,
        size_t len);
 
+/**
+ * cfg80211_send_unprot_deauth - notification of unprotected deauthentication
+ * @dev: network device
+ * @buf: deauthentication frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever a received Deauthentication frame has been
+ * dropped in station mode because of MFP being used but the Deauthentication
+ * frame was not protected. This function may sleep.
+ */
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
+                                size_t len);
+
+/**
+ * cfg80211_send_unprot_disassoc - notification of unprotected disassociation
+ * @dev: network device
+ * @buf: disassociation frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever a received Disassociation frame has been
+ * dropped in station mode because of MFP being used but the Disassociation
+ * frame was not protected. This function may sleep.
+ */
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
+                                  size_t len);
+
 /**
  * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP)
  * @dev: network device
index 052789ef474539d39fdb9d5567f90534fddb38af..4573ce1e1d15627aa23d1279c1c2878c32a09c28 100644 (file)
@@ -1540,12 +1540,30 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
        if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) {
                if (unlikely(!ieee80211_has_protected(fc) &&
                             ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
-                            rx->key))
+                            rx->key)) {
+                       if (ieee80211_is_deauth(fc))
+                               cfg80211_send_unprot_deauth(rx->sdata->dev,
+                                                           rx->skb->data,
+                                                           rx->skb->len);
+                       else if (ieee80211_is_disassoc(fc))
+                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
+                                                             rx->skb->data,
+                                                             rx->skb->len);
                        return -EACCES;
+               }
                /* BIP does not use Protected field, so need to check MMIE */
                if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
-                            ieee80211_get_mmie_keyidx(rx->skb) < 0))
+                            ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
+                       if (ieee80211_is_deauth(fc))
+                               cfg80211_send_unprot_deauth(rx->sdata->dev,
+                                                           rx->skb->data,
+                                                           rx->skb->len);
+                       else if (ieee80211_is_disassoc(fc))
+                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
+                                                             rx->skb->data,
+                                                             rx->skb->len);
                        return -EACCES;
+               }
                /*
                 * When using MFP, Action frames are not allowed prior to
                 * having configured keys.
index d7680f2a4c5bd0e1ef4f3fabc9ca58914cf50f7f..aa5df8865ff75a87c2e905fa329988b63f39391e 100644 (file)
@@ -263,6 +263,28 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
 }
 EXPORT_SYMBOL(cfg80211_send_disassoc);
 
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
+                                size_t len)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);
+}
+EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
+
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
+                                  size_t len)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);
+}
+EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
+
 static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr)
 {
        int i;
index 594a6ac8b9d2d423119536b89c8298d5e888158b..aefce54d47e2a2307610625c685e49891c625de6 100644 (file)
@@ -5473,6 +5473,22 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
+void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
+                               struct net_device *netdev, const u8 *buf,
+                               size_t len, gfp_t gfp)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
+}
+
+void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
+                                 struct net_device *netdev, const u8 *buf,
+                                 size_t len, gfp_t gfp)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
+}
+
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
                                      const u8 *addr, gfp_t gfp)
index 16c2f719076824e2413364d35e7396ba087400b7..e3f7fa886966ee39b0f508b40c376f9695a1d9ca 100644 (file)
@@ -25,6 +25,12 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
 void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                           struct net_device *netdev,
                           const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
+                               struct net_device *netdev,
+                               const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
+                                 struct net_device *netdev,
+                                 const u8 *buf, size_t len, gfp_t gfp);
 void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
                               struct net_device *netdev,
                               const u8 *addr, gfp_t gfp);