cfg80211: keep track of current_bss for userspace SME
[safe/jmp/linux-2.6] / net / wireless / sme.c
index df9173f..3728d2b 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
 #include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
 #include <net/cfg80211.h>
 #include <net/rtnetlink.h>
 #include "nl80211.h"
@@ -86,7 +88,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
                wdev->conn->params.ssid_len);
        request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
 
-       request->ifidx = wdev->netdev->ifindex;
+       request->dev = wdev->netdev;
        request->wiphy = &rdev->wiphy;
 
        rdev->scan_req = request;
@@ -95,6 +97,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
        if (!err) {
                wdev->conn->state = CFG80211_CONN_SCANNING;
                nl80211_send_scan_start(rdev, wdev->netdev);
+               dev_hold(wdev->netdev);
        } else {
                rdev->scan_req = NULL;
                kfree(request);
@@ -125,7 +128,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                            params->channel, params->auth_type,
                                            params->bssid,
                                            params->ssid, params->ssid_len,
-                                           NULL, 0);
+                                           NULL, 0,
+                                           params->key, params->key_len,
+                                           params->key_idx);
        case CFG80211_CONN_ASSOCIATE_NEXT:
                BUG_ON(!rdev->ops->assoc);
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -177,7 +182,7 @@ void cfg80211_conn_work(struct work_struct *work)
                                        wdev->conn->params.bssid,
                                        NULL, 0, NULL, 0,
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                       false);
+                                       false, NULL);
                wdev_unlock(wdev);
        }
 
@@ -225,7 +230,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
        if (wdev->sme_state != CFG80211_SME_CONNECTING)
                return;
 
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn)
                return;
 
        if (wdev->conn->state != CFG80211_CONN_SCANNING &&
@@ -242,7 +247,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
                                        wdev->conn->params.bssid,
                                        NULL, 0, NULL, 0,
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                       false);
+                                       false, NULL);
        }
 }
 
@@ -279,8 +284,12 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
                /* select automatically between only open, shared, leap */
                switch (wdev->conn->params.auth_type) {
                case NL80211_AUTHTYPE_OPEN_SYSTEM:
-                       wdev->conn->params.auth_type =
-                               NL80211_AUTHTYPE_SHARED_KEY;
+                       if (wdev->connect_keys)
+                               wdev->conn->params.auth_type =
+                                       NL80211_AUTHTYPE_SHARED_KEY;
+                       else
+                               wdev->conn->params.auth_type =
+                                       NL80211_AUTHTYPE_NETWORK_EAP;
                        break;
                case NL80211_AUTHTYPE_SHARED_KEY:
                        wdev->conn->params.auth_type =
@@ -295,9 +304,8 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
                wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
                schedule_work(&rdev->conn_work);
        } else if (status_code != WLAN_STATUS_SUCCESS) {
-               wdev->sme_state = CFG80211_SME_IDLE;
-               kfree(wdev->conn);
-               wdev->conn = NULL;
+               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
+                                         status_code, false, NULL);
        } else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
                 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
                wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
@@ -308,10 +316,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
 void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                               const u8 *req_ie, size_t req_ie_len,
                               const u8 *resp_ie, size_t resp_ie_len,
-                              u16 status, bool wextev)
+                              u16 status, bool wextev,
+                              struct cfg80211_bss *bss)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_bss *bss;
 #ifdef CONFIG_WIRELESS_EXT
        union iwreq_data wrqu;
 #endif
@@ -336,7 +344,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                if (req_ie && status == WLAN_STATUS_SUCCESS) {
                        memset(&wrqu, 0, sizeof(wrqu));
                        wrqu.data.length = req_ie_len;
-                       wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie);
+                       wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
                }
 
                if (resp_ie && status == WLAN_STATUS_SUCCESS) {
@@ -353,42 +361,46 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        }
 #endif
 
-       if (status == WLAN_STATUS_SUCCESS &&
-           wdev->sme_state == CFG80211_SME_IDLE) {
-               wdev->sme_state = CFG80211_SME_CONNECTED;
-               return;
-       }
-
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
        if (wdev->current_bss) {
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(&wdev->current_bss->pub);
                wdev->current_bss = NULL;
        }
 
+       if (status == WLAN_STATUS_SUCCESS &&
+           wdev->sme_state == CFG80211_SME_IDLE)
+               goto success;
+
+       if (wdev->sme_state != CFG80211_SME_CONNECTING)
+               return;
+
        if (wdev->conn)
                wdev->conn->state = CFG80211_CONN_IDLE;
 
-       if (status == WLAN_STATUS_SUCCESS) {
+       if (status != WLAN_STATUS_SUCCESS) {
+               wdev->sme_state = CFG80211_SME_IDLE;
+               kfree(wdev->conn);
+               wdev->conn = NULL;
+               kfree(wdev->connect_keys);
+               wdev->connect_keys = NULL;
+               return;
+       }
+
+ success:
+       if (!bss)
                bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
                                       wdev->ssid, wdev->ssid_len,
                                       WLAN_CAPABILITY_ESS,
                                       WLAN_CAPABILITY_ESS);
 
-               if (WARN_ON(!bss))
-                       return;
+       if (WARN_ON(!bss))
+               return;
 
-               cfg80211_hold_bss(bss_from_pub(bss));
-               wdev->current_bss = bss_from_pub(bss);
+       cfg80211_hold_bss(bss_from_pub(bss));
+       wdev->current_bss = bss_from_pub(bss);
 
-               wdev->sme_state = CFG80211_SME_CONNECTED;
-       } else {
-               wdev->sme_state = CFG80211_SME_IDLE;
-               kfree(wdev->conn);
-               wdev->conn = NULL;
-       }
+       wdev->sme_state = CFG80211_SME_CONNECTED;
+       cfg80211_upload_connect_keys(wdev);
 }
 
 void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
@@ -466,7 +478,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
        if (req_ie) {
                memset(&wrqu, 0, sizeof(wrqu));
                wrqu.data.length = req_ie_len;
-               wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
+               wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
                                    &wrqu, req_ie);
        }
 
@@ -517,6 +529,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                             size_t ie_len, u16 reason, bool from_ap)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       int i;
 #ifdef CONFIG_WIRELESS_EXT
        union iwreq_data wrqu;
 #endif
@@ -544,8 +558,15 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                wdev->conn = NULL;
        }
 
-       nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev,
-                                 reason, ie, ie_len, from_ap);
+       nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
+
+       /*
+        * Delete all the keys ... pairwise keys can't really
+        * exist any more anyway, but default keys might.
+        */
+       if (rdev->ops->del_key)
+               for (i = 0; i < 6; i++)
+                       rdev->ops->del_key(wdev->wiphy, dev, i, NULL);
 
 #ifdef CONFIG_WIRELESS_EXT
        memset(&wrqu, 0, sizeof(wrqu));
@@ -581,7 +602,8 @@ EXPORT_SYMBOL(cfg80211_disconnected);
 
 int __cfg80211_connect(struct cfg80211_registered_device *rdev,
                       struct net_device *dev,
-                      struct cfg80211_connect_params *connect)
+                      struct cfg80211_connect_params *connect,
+                      struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
@@ -591,6 +613,24 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
        if (wdev->sme_state != CFG80211_SME_IDLE)
                return -EALREADY;
 
+       if (WARN_ON(wdev->connect_keys)) {
+               kfree(wdev->connect_keys);
+               wdev->connect_keys = NULL;
+       }
+
+       if (connkeys && connkeys->def >= 0) {
+               int idx;
+
+               idx = connkeys->def;
+               /* If given a WEP key we may need it for shared key auth */
+               if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                   connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) {
+                       connect->key_idx = idx;
+                       connect->key = connkeys->params[idx].key;
+                       connect->key_len = connkeys->params[idx].key_len;
+               }
+       }
+
        if (!rdev->ops->connect) {
                if (!rdev->ops->auth || !rdev->ops->assoc)
                        return -EOPNOTSUPP;
@@ -641,6 +681,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
                        cfg80211_get_conn_bss(wdev);
 
                wdev->sme_state = CFG80211_SME_CONNECTING;
+               wdev->connect_keys = connkeys;
 
                /* we're good if we have both BSSID and channel */
                if (wdev->conn->params.bssid && wdev->conn->params.channel) {
@@ -663,13 +704,16 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
                        kfree(wdev->conn);
                        wdev->conn = NULL;
                        wdev->sme_state = CFG80211_SME_IDLE;
+                       wdev->connect_keys = NULL;
                }
 
                return err;
        } else {
                wdev->sme_state = CFG80211_SME_CONNECTING;
+               wdev->connect_keys = connkeys;
                err = rdev->ops->connect(&rdev->wiphy, dev, connect);
                if (err) {
+                       wdev->connect_keys = NULL;
                        wdev->sme_state = CFG80211_SME_IDLE;
                        return err;
                }
@@ -683,12 +727,13 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 
 int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     struct net_device *dev,
-                    struct cfg80211_connect_params *connect)
+                    struct cfg80211_connect_params *connect,
+                    struct cfg80211_cached_keys *connkeys)
 {
        int err;
 
        wdev_lock(dev->ieee80211_ptr);
-       err = __cfg80211_connect(rdev, dev, connect);
+       err = __cfg80211_connect(rdev, dev, connect, connkeys);
        wdev_unlock(dev->ieee80211_ptr);
 
        return err;
@@ -705,6 +750,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
        if (wdev->sme_state == CFG80211_SME_IDLE)
                return -EINVAL;
 
+       kfree(wdev->connect_keys);
+       wdev->connect_keys = NULL;
+
        if (!rdev->ops->disconnect) {
                if (!rdev->ops->deauth)
                        return -EOPNOTSUPP;
@@ -741,7 +789,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
        else if (wdev->sme_state == CFG80211_SME_CONNECTING)
                __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
                                          WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         wextev);
+                                         wextev, NULL);
 
        return 0;
 }
@@ -782,8 +830,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx)
                return;
 
        memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
-       if (cfg80211_mlme_deauth(rdev, dev, bssid,
-                                NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
+       if (__cfg80211_mlme_deauth(rdev, dev, bssid,
+                                  NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
                /* whatever -- assume gone anyway */
                cfg80211_unhold_bss(wdev->auth_bsses[idx]);
                cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);