]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/wireless/sme.c
cfg80211: validate channel settings across interfaces
[net-next-2.6.git] / net / wireless / sme.c
index e7a8851093a7087fb3827e89e3f1dc093a41983b..219c3bc2c37d3c1a3bf88a0b5aae1b11751d2602 100644 (file)
@@ -8,9 +8,12 @@
 #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"
+#include "reg.h"
 
 struct cfg80211_conn {
        struct cfg80211_connect_params params;
@@ -86,7 +89,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 +98,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);
@@ -179,7 +183,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);
        }
 
@@ -244,7 +248,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);
        }
 }
 
@@ -252,9 +256,11 @@ void cfg80211_sme_scan_done(struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
 
+       mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
        wdev_lock(wdev);
        __cfg80211_sme_scan_done(dev);
        wdev_unlock(wdev);
+       mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
 }
 
 void cfg80211_sme_rx_auth(struct net_device *dev,
@@ -302,7 +308,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
                schedule_work(&rdev->conn_work);
        } else if (status_code != WLAN_STATUS_SUCCESS) {
                __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
-                                         status_code, false);
+                                         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;
@@ -313,10 +319,11 @@ 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;
+       u8 *country_ie;
 #ifdef CONFIG_WIRELESS_EXT
        union iwreq_data wrqu;
 #endif
@@ -341,7 +348,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) {
@@ -358,6 +365,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        }
 #endif
 
+       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;
@@ -365,12 +378,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        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 (wdev->conn)
                wdev->conn->state = CFG80211_CONN_IDLE;
 
@@ -380,13 +387,16 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                wdev->conn = NULL;
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
+               wdev->ssid_len = 0;
                return;
        }
 
-       bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
-                              wdev->ssid, wdev->ssid_len,
-                              WLAN_CAPABILITY_ESS,
-                              WLAN_CAPABILITY_ESS);
+ 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;
@@ -394,9 +404,22 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
- success:
        wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
+
+       country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+
+       if (!country_ie)
+               return;
+
+       /*
+        * ieee80211_bss_get_ie() ensures we can access:
+        * - country_ie + 2, the start of the country ie data, and
+        * - and country_ie[1] which is the IE length
+        */
+       regulatory_hint_11d(wdev->wiphy,
+                           country_ie + 2,
+                           country_ie[1]);
 }
 
 void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
@@ -474,7 +497,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);
        }
 
@@ -546,12 +569,33 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
 
        wdev->current_bss = NULL;
        wdev->sme_state = CFG80211_SME_IDLE;
+       wdev->ssid_len = 0;
 
        if (wdev->conn) {
+               const u8 *bssid;
+               int ret;
+
                kfree(wdev->conn->ie);
                wdev->conn->ie = NULL;
                kfree(wdev->conn);
                wdev->conn = NULL;
+
+               /*
+                * If this disconnect was due to a disassoc, we
+                * we might still have an auth BSS around. For
+                * the userspace SME that's currently expected,
+                * but for the kernel SME (nl80211 CONNECT or
+                * wireless extensions) we want to clear up all
+                * state.
+                */
+               for (i = 0; i < MAX_AUTH_BSSES; i++) {
+                       if (!wdev->auth_bsses[i])
+                               continue;
+                       bssid = wdev->auth_bsses[i]->pub.bssid;
+                       ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
+                                               WLAN_REASON_DEAUTH_LEAVING);
+                       WARN(ret, "deauth failed: %d\n", ret);
+               }
        }
 
        nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
@@ -602,6 +646,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
                       struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_channel *chan;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -609,6 +654,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
        if (wdev->sme_state != CFG80211_SME_IDLE)
                return -EALREADY;
 
+       chan = rdev_fixed_channel(rdev, wdev);
+       if (chan && chan != connect->channel)
+               return -EBUSY;
+
        if (WARN_ON(wdev->connect_keys)) {
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
@@ -616,14 +665,28 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 
        if (connkeys && connkeys->def >= 0) {
                int idx;
+               u32 cipher;
 
                idx = connkeys->def;
+               cipher = connkeys->params[idx].cipher;
                /* 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) {
+               if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                   cipher == WLAN_CIPHER_SUITE_WEP104) {
                        connect->key_idx = idx;
                        connect->key = connkeys->params[idx].key;
                        connect->key_len = connkeys->params[idx].key_len;
+
+                       /*
+                        * If ciphers are not set (e.g. when going through
+                        * iwconfig), we have to set them appropriately here.
+                        */
+                       if (connect->crypto.cipher_group == 0)
+                               connect->crypto.cipher_group = cipher;
+
+                       if (connect->crypto.n_ciphers_pairwise == 0) {
+                               connect->crypto.n_ciphers_pairwise = 1;
+                               connect->crypto.ciphers_pairwise[0] = cipher;
+                       }
                }
        }
 
@@ -701,6 +764,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
                        wdev->conn = NULL;
                        wdev->sme_state = CFG80211_SME_IDLE;
                        wdev->connect_keys = NULL;
+                       wdev->ssid_len = 0;
                }
 
                return err;
@@ -728,9 +792,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
 {
        int err;
 
+       mutex_lock(&rdev->devlist_mtx);
        wdev_lock(dev->ieee80211_ptr);
        err = __cfg80211_connect(rdev, dev, connect, connkeys);
        wdev_unlock(dev->ieee80211_ptr);
+       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
@@ -765,6 +831,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                        wdev->sme_state = CFG80211_SME_IDLE;
                        kfree(wdev->conn);
                        wdev->conn = NULL;
+                       wdev->ssid_len = 0;
                        return 0;
                }
 
@@ -785,7 +852,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;
 }