cfg80211: Add local-state-change-only auth/deauth/disassoc
authorJouni Malinen <j@w1.fi>
Sun, 4 Apr 2010 06:37:19 +0000 (09:37 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 7 Apr 2010 18:37:56 +0000 (14:37 -0400)
cfg80211 is quite strict on allowing authentication and association
commands only in certain states. In order to meet these requirements,
user space applications may need to clear authentication or
association state in some cases. Currently, this can be done with
deauth/disassoc command, but that ends up sending out Deauthentication
or Disassociation frame unnecessarily. Add a new nl80211 attribute to
allow this sending of the frame be skipped, but with all other
deauth/disassoc operations being completed.

Similar state change is also needed for IEEE 802.11r FT protocol in
the FT-over-DS case which does not use Authentication frame exchange
in a transition to another BSS. For this to work with cfg80211, an
authentication entry needs to be created for the target BSS without
sending out an Authentication frame. The nl80211 authentication
command can be used for this purpose, too, with the new attribute to
indicate that the command is only for changing local state. This
enables wpa_supplicant to complete FT-over-DS transition successfully.

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/mlme.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/sme.c

index daf6a3432b92e804a504cca46e4257fb36df2244..2ea3edeee7aae9143db54ed1d12c030d8cfca13b 100644 (file)
@@ -703,6 +703,12 @@ enum nl80211_commands {
  * @NL80211_ATTR_CQM: connection quality monitor configuration in a
  *     nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
  *
+ * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command
+ *     is requesting a local authentication/association state change without
+ *     invoking actual management frame exchange. This can be used with
+ *     NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
+ *     NL80211_CMD_DISASSOCIATE.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -856,6 +862,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_CQM,
 
+       NL80211_ATTR_LOCAL_STATE_CHANGE,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 868cfd3b972480287eb7ab56cbfa3a0de9baaba9..37cebd3aa0f7d3f57d9a89b4f617e66a406676ee 100644 (file)
@@ -704,6 +704,10 @@ struct cfg80211_crypto_settings {
  * @key_len: length of WEP key for shared key authentication
  * @key_idx: index of WEP key for shared key authentication
  * @key: WEP key for shared key authentication
+ * @local_state_change: This is a request for a local state only, i.e., no
+ *     Authentication frame is to be transmitted and authentication state is
+ *     to be changed without having to wait for a response from the peer STA
+ *     (AP).
  */
 struct cfg80211_auth_request {
        struct cfg80211_bss *bss;
@@ -712,6 +716,7 @@ struct cfg80211_auth_request {
        enum nl80211_auth_type auth_type;
        const u8 *key;
        u8 key_len, key_idx;
+       bool local_state_change;
 };
 
 /**
@@ -744,12 +749,15 @@ struct cfg80211_assoc_request {
  * @ie: Extra IEs to add to Deauthentication frame or %NULL
  * @ie_len: Length of ie buffer in octets
  * @reason_code: The reason code for the deauthentication
+ * @local_state_change: This is a request for a local state only, i.e., no
+ *     Deauthentication frame is to be transmitted.
  */
 struct cfg80211_deauth_request {
        struct cfg80211_bss *bss;
        const u8 *ie;
        size_t ie_len;
        u16 reason_code;
+       bool local_state_change;
 };
 
 /**
@@ -762,12 +770,15 @@ struct cfg80211_deauth_request {
  * @ie: Extra IEs to add to Disassociation frame or %NULL
  * @ie_len: Length of ie buffer in octets
  * @reason_code: The reason code for the disassociation
+ * @local_state_change: This is a request for a local state only, i.e., no
+ *     Disassociation frame is to be transmitted.
  */
 struct cfg80211_disassoc_request {
        struct cfg80211_bss *bss;
        const u8 *ie;
        size_t ie_len;
        u16 reason_code;
+       bool local_state_change;
 };
 
 /**
index 57a3c62139e24e50d7e960e3bbed24a6225c5ee2..4c189d0be4a3ba526beb1290440def362c28aee5 100644 (file)
@@ -210,7 +210,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
 
 static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                           const u8 *bssid, u16 stype, u16 reason,
-                                          void *cookie)
+                                          void *cookie, bool send_frame)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -247,7 +247,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                        cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
        if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-       ieee80211_tx_skb(sdata, skb);
+
+       if (send_frame)
+               ieee80211_tx_skb(sdata, skb);
+       else
+               kfree_skb(skb);
 }
 
 void ieee80211_send_pspoll(struct ieee80211_local *local,
@@ -980,7 +984,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
        ieee80211_send_deauth_disassoc(sdata, bssid,
                                       IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                                      NULL);
+                                      NULL, true);
 }
 
 void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@ -1724,7 +1728,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                        ieee80211_send_deauth_disassoc(sdata, bssid,
                                        IEEE80211_STYPE_DEAUTH,
                                        WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                                       NULL);
+                                       NULL, true);
                        mutex_lock(&ifmgd->mtx);
                }
        }
@@ -1908,6 +1912,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_work *wk;
        u16 auth_alg;
 
+       if (req->local_state_change)
+               return 0; /* no need to update mac80211 state */
+
        switch (req->auth_type) {
        case NL80211_AUTHTYPE_OPEN_SYSTEM:
                auth_alg = WLAN_AUTH_OPEN;
@@ -2163,9 +2170,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
               sdata->name, bssid, req->reason_code);
 
-       ieee80211_send_deauth_disassoc(sdata, bssid,
-                       IEEE80211_STYPE_DEAUTH, req->reason_code,
-                       cookie);
+       ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH,
+                                      req->reason_code, cookie,
+                                      !req->local_state_change);
 
        ieee80211_recalc_idle(sdata->local);
 
@@ -2202,7 +2209,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
                        IEEE80211_STYPE_DISASSOC, req->reason_code,
-                       cookie);
+                       cookie, !req->local_state_change);
        sta_info_destroy_addr(sdata, bssid);
 
        ieee80211_recalc_idle(sdata->local);
index d52da913145a82c96c1be83f84a7771909da8763..b2234b436ead117d4fee49cd56b27fbfa6d36f55 100644 (file)
@@ -293,13 +293,15 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
                         const u8 *bssid,
                         const u8 *ssid, int ssid_len,
                         const u8 *ie, int ie_len,
-                        const u8 *key, int key_len, int key_idx);
+                        const u8 *key, int key_len, int key_idx,
+                        bool local_state_change);
 int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
                       struct net_device *dev, struct ieee80211_channel *chan,
                       enum nl80211_auth_type auth_type, const u8 *bssid,
                       const u8 *ssid, int ssid_len,
                       const u8 *ie, int ie_len,
-                      const u8 *key, int key_len, int key_idx);
+                      const u8 *key, int key_len, int key_idx,
+                      bool local_state_change);
 int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
                          struct net_device *dev,
                          struct ieee80211_channel *chan,
@@ -315,13 +317,16 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
                        struct cfg80211_crypto_settings *crypt);
 int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
                           struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason);
+                          const u8 *ie, int ie_len, u16 reason,
+                          bool local_state_change);
 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
                         struct net_device *dev, const u8 *bssid,
-                        const u8 *ie, int ie_len, u16 reason);
+                        const u8 *ie, int ie_len, u16 reason,
+                        bool local_state_change);
 int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                           struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason);
+                          const u8 *ie, int ie_len, u16 reason,
+                          bool local_state_change);
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
 void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
index 0855f0d32349fce237999c8a6318cd2c5d8b354f..387dd2a27d2f5a9a91d8e3d9b111336b8545c03d 100644 (file)
@@ -377,7 +377,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
                         const u8 *bssid,
                         const u8 *ssid, int ssid_len,
                         const u8 *ie, int ie_len,
-                        const u8 *key, int key_len, int key_idx)
+                        const u8 *key, int key_len, int key_idx,
+                        bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_auth_request req;
@@ -407,6 +408,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
 
        memset(&req, 0, sizeof(req));
 
+       req.local_state_change = local_state_change;
        req.ie = ie;
        req.ie_len = ie_len;
        req.auth_type = auth_type;
@@ -433,12 +435,18 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
                goto out;
        }
 
-       wdev->authtry_bsses[slot] = bss;
+       if (local_state_change)
+               wdev->auth_bsses[slot] = bss;
+       else
+               wdev->authtry_bsses[slot] = bss;
        cfg80211_hold_bss(bss);
 
        err = rdev->ops->auth(&rdev->wiphy, dev, &req);
        if (err) {
-               wdev->authtry_bsses[slot] = NULL;
+               if (local_state_change)
+                       wdev->auth_bsses[slot] = NULL;
+               else
+                       wdev->authtry_bsses[slot] = NULL;
                cfg80211_unhold_bss(bss);
        }
 
@@ -453,14 +461,15 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
                       enum nl80211_auth_type auth_type, const u8 *bssid,
                       const u8 *ssid, int ssid_len,
                       const u8 *ie, int ie_len,
-                      const u8 *key, int key_len, int key_idx)
+                      const u8 *key, int key_len, int key_idx,
+                      bool local_state_change)
 {
        int err;
 
        wdev_lock(dev->ieee80211_ptr);
        err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
                                   ssid, ssid_len, ie, ie_len,
-                                  key, key_len, key_idx);
+                                  key, key_len, key_idx, local_state_change);
        wdev_unlock(dev->ieee80211_ptr);
 
        return err;
@@ -554,7 +563,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 
 int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
                           struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason)
+                          const u8 *ie, int ie_len, u16 reason,
+                          bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_deauth_request req;
@@ -564,6 +574,7 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
 
        memset(&req, 0, sizeof(req));
        req.reason_code = reason;
+       req.local_state_change = local_state_change;
        req.ie = ie;
        req.ie_len = ie_len;
        if (wdev->current_bss &&
@@ -590,13 +601,15 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
 
 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
                         struct net_device *dev, const u8 *bssid,
-                        const u8 *ie, int ie_len, u16 reason)
+                        const u8 *ie, int ie_len, u16 reason,
+                        bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
        wdev_lock(wdev);
-       err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
+       err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason,
+                                    local_state_change);
        wdev_unlock(wdev);
 
        return err;
@@ -604,7 +617,8 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
 
 static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                                    struct net_device *dev, const u8 *bssid,
-                                   const u8 *ie, int ie_len, u16 reason)
+                                   const u8 *ie, int ie_len, u16 reason,
+                                   bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_disassoc_request req;
@@ -619,6 +633,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
 
        memset(&req, 0, sizeof(req));
        req.reason_code = reason;
+       req.local_state_change = local_state_change;
        req.ie = ie;
        req.ie_len = ie_len;
        if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
@@ -631,13 +646,15 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
 
 int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                           struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason)
+                          const u8 *ie, int ie_len, u16 reason,
+                          bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
        wdev_lock(wdev);
-       err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
+       err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason,
+                                      local_state_change);
        wdev_unlock(wdev);
 
        return err;
index 95149f3034091c327844c1920ff8d45807278481..df5505b3930c49b70934db9c865dc7ac7003556b 100644 (file)
@@ -150,6 +150,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
        [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
+       [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
 };
 
 /* policy for the attributes */
@@ -3393,6 +3394,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        int err, ssid_len, ie_len = 0;
        enum nl80211_auth_type auth_type;
        struct key_parse key;
+       bool local_state_change;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -3471,9 +3473,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
+       local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
+
        err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
                                 ssid, ssid_len, ie, ie_len,
-                                key.p.key, key.p.key_len, key.idx);
+                                key.p.key, key.p.key_len, key.idx,
+                                local_state_change);
 
 out:
        cfg80211_unlock_rdev(rdev);
@@ -3650,6 +3655,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
        const u8 *ie = NULL, *bssid;
        int err, ie_len = 0;
        u16 reason_code;
+       bool local_state_change;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -3695,7 +3701,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
                ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code);
+       local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
+
+       err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
+                                  local_state_change);
 
 out:
        cfg80211_unlock_rdev(rdev);
@@ -3712,6 +3721,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
        const u8 *ie = NULL, *bssid;
        int err, ie_len = 0;
        u16 reason_code;
+       bool local_state_change;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -3757,7 +3767,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
                ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code);
+       local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
+
+       err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
+                                    local_state_change);
 
 out:
        cfg80211_unlock_rdev(rdev);
index 17fde0da1b08658db9882f000a51928dbd9ad339..17465777eb476578487d495abc28efa3d163ab66 100644 (file)
@@ -170,7 +170,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                            params->ssid, params->ssid_len,
                                            NULL, 0,
                                            params->key, params->key_len,
-                                           params->key_idx);
+                                           params->key_idx, false);
        case CFG80211_CONN_ASSOCIATE_NEXT:
                BUG_ON(!rdev->ops->assoc);
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -185,12 +185,13 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                if (err)
                        __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                               NULL, 0,
-                                              WLAN_REASON_DEAUTH_LEAVING);
+                                              WLAN_REASON_DEAUTH_LEAVING,
+                                              false);
                return err;
        case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
                __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                       NULL, 0,
-                                      WLAN_REASON_DEAUTH_LEAVING);
+                                      WLAN_REASON_DEAUTH_LEAVING, false);
                /* return an error so that we call __cfg80211_connect_result() */
                return -EINVAL;
        default:
@@ -675,7 +676,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                                continue;
                        bssid = wdev->auth_bsses[i]->pub.bssid;
                        ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
-                                               WLAN_REASON_DEAUTH_LEAVING);
+                                               WLAN_REASON_DEAUTH_LEAVING,
+                                               false);
                        WARN(ret, "deauth failed: %d\n", ret);
                }
        }
@@ -934,7 +936,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                /* wdev->conn->params.bssid must be set if > SCANNING */
                err = __cfg80211_mlme_deauth(rdev, dev,
                                             wdev->conn->params.bssid,
-                                            NULL, 0, reason);
+                                            NULL, 0, reason, false);
                if (err)
                        return err;
        } else {
@@ -990,7 +992,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx)
 
        memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
        if (__cfg80211_mlme_deauth(rdev, dev, bssid,
-                                  NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
+                                  NULL, 0, WLAN_REASON_DEAUTH_LEAVING,
+                                  false)) {
                /* whatever -- assume gone anyway */
                cfg80211_unhold_bss(wdev->auth_bsses[idx]);
                cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);