cfg80211: Fix signal_type comparison
[safe/jmp/linux-2.6] / net / wireless / scan.c
index d03447d..58401d2 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
  */
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
@@ -88,7 +89,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
        WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
 
        request->aborted = aborted;
-       schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk);
+       queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
 }
 EXPORT_SYMBOL(cfg80211_scan_done);
 
@@ -100,8 +101,10 @@ static void bss_release(struct kref *ref)
        if (bss->pub.free_priv)
                bss->pub.free_priv(&bss->pub);
 
-       if (bss->ies_allocated)
-               kfree(bss->pub.information_elements);
+       if (bss->beacon_ies_allocated)
+               kfree(bss->pub.beacon_ies);
+       if (bss->proberesp_ies_allocated)
+               kfree(bss->pub.proberesp_ies);
 
        BUG_ON(atomic_read(&bss->hold));
 
@@ -141,9 +144,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
                dev->bss_generation++;
 }
 
-static u8 *find_ie(u8 num, u8 *ies, int len)
+const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
 {
-       while (len > 2 && ies[0] != num) {
+       while (len > 2 && ies[0] != eid) {
                len -= ies[1] + 2;
                ies += ies[1] + 2;
        }
@@ -153,11 +156,12 @@ static u8 *find_ie(u8 num, u8 *ies, int len)
                return NULL;
        return ies;
 }
+EXPORT_SYMBOL(cfg80211_find_ie);
 
 static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
 {
-       const u8 *ie1 = find_ie(num, ies1, len1);
-       const u8 *ie2 = find_ie(num, ies2, len2);
+       const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
+       const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
        int r;
 
        if (!ie1 && !ie2)
@@ -183,9 +187,9 @@ static bool is_bss(struct cfg80211_bss *a,
        if (!ssid)
                return true;
 
-       ssidie = find_ie(WLAN_EID_SSID,
-                        a->information_elements,
-                        a->len_information_elements);
+       ssidie = cfg80211_find_ie(WLAN_EID_SSID,
+                                 a->information_elements,
+                                 a->len_information_elements);
        if (!ssidie)
                return false;
        if (ssidie[1] != ssid_len)
@@ -202,9 +206,9 @@ static bool is_mesh(struct cfg80211_bss *a,
        if (!is_zero_ether_addr(a->bssid))
                return false;
 
-       ie = find_ie(WLAN_EID_MESH_ID,
-                    a->information_elements,
-                    a->len_information_elements);
+       ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
+                             a->information_elements,
+                             a->len_information_elements);
        if (!ie)
                return false;
        if (ie[1] != meshidlen)
@@ -212,9 +216,9 @@ static bool is_mesh(struct cfg80211_bss *a,
        if (memcmp(ie + 2, meshid, meshidlen))
                return false;
 
-       ie = find_ie(WLAN_EID_MESH_CONFIG,
-                    a->information_elements,
-                    a->len_information_elements);
+       ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
+                             a->information_elements,
+                             a->len_information_elements);
        if (!ie)
                return false;
        if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@@ -375,8 +379,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
 
 static struct cfg80211_internal_bss *
 cfg80211_bss_update(struct cfg80211_registered_device *dev,
-                   struct cfg80211_internal_bss *res,
-                   bool overwrite)
+                   struct cfg80211_internal_bss *res)
 {
        struct cfg80211_internal_bss *found = NULL;
        const u8 *meshid, *meshcfg;
@@ -394,11 +397,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
 
        if (is_zero_ether_addr(res->pub.bssid)) {
                /* must be mesh, verify */
-               meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements,
-                                res->pub.len_information_elements);
-               meshcfg = find_ie(WLAN_EID_MESH_CONFIG,
-                                 res->pub.information_elements,
-                                 res->pub.len_information_elements);
+               meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
+                                         res->pub.information_elements,
+                                         res->pub.len_information_elements);
+               meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
+                                          res->pub.information_elements,
+                                          res->pub.len_information_elements);
                if (!meshid || !meshcfg ||
                    meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) {
                        /* bogus mesh */
@@ -418,28 +422,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                found->pub.capability = res->pub.capability;
                found->ts = res->ts;
 
-               /* overwrite IEs */
-               if (overwrite) {
+               /* Update IEs */
+               if (res->pub.proberesp_ies) {
                        size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
-                       size_t ielen = res->pub.len_information_elements;
+                       size_t ielen = res->pub.len_proberesp_ies;
+
+                       if (found->pub.proberesp_ies &&
+                           !found->proberesp_ies_allocated &&
+                           ksize(found) >= used + ielen) {
+                               memcpy(found->pub.proberesp_ies,
+                                      res->pub.proberesp_ies, ielen);
+                               found->pub.len_proberesp_ies = ielen;
+                       } else {
+                               u8 *ies = found->pub.proberesp_ies;
+
+                               if (found->proberesp_ies_allocated)
+                                       ies = krealloc(ies, ielen, GFP_ATOMIC);
+                               else
+                                       ies = kmalloc(ielen, GFP_ATOMIC);
+
+                               if (ies) {
+                                       memcpy(ies, res->pub.proberesp_ies,
+                                              ielen);
+                                       found->proberesp_ies_allocated = true;
+                                       found->pub.proberesp_ies = ies;
+                                       found->pub.len_proberesp_ies = ielen;
+                               }
+                       }
 
-                       if (!found->ies_allocated && ksize(found) >= used + ielen) {
-                               memcpy(found->pub.information_elements,
-                                      res->pub.information_elements, ielen);
-                               found->pub.len_information_elements = ielen;
+                       /* Override possible earlier Beacon frame IEs */
+                       found->pub.information_elements =
+                               found->pub.proberesp_ies;
+                       found->pub.len_information_elements =
+                               found->pub.len_proberesp_ies;
+               }
+               if (res->pub.beacon_ies) {
+                       size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
+                       size_t ielen = res->pub.len_beacon_ies;
+
+                       if (found->pub.beacon_ies &&
+                           !found->beacon_ies_allocated &&
+                           ksize(found) >= used + ielen) {
+                               memcpy(found->pub.beacon_ies,
+                                      res->pub.beacon_ies, ielen);
+                               found->pub.len_beacon_ies = ielen;
                        } else {
-                               u8 *ies = found->pub.information_elements;
+                               u8 *ies = found->pub.beacon_ies;
 
-                               if (found->ies_allocated)
+                               if (found->beacon_ies_allocated)
                                        ies = krealloc(ies, ielen, GFP_ATOMIC);
                                else
                                        ies = kmalloc(ielen, GFP_ATOMIC);
 
                                if (ies) {
-                                       memcpy(ies, res->pub.information_elements, ielen);
-                                       found->ies_allocated = true;
-                                       found->pub.information_elements = ies;
-                                       found->pub.len_information_elements = ielen;
+                                       memcpy(ies, res->pub.beacon_ies,
+                                              ielen);
+                                       found->beacon_ies_allocated = true;
+                                       found->pub.beacon_ies = ies;
+                                       found->pub.len_beacon_ies = ielen;
                                }
                        }
                }
@@ -475,7 +515,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
 
        privsz = wiphy->bss_priv_size;
 
-       if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
+       if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
                        (signal < 0 || signal > 100)))
                return NULL;
 
@@ -489,14 +529,26 @@ cfg80211_inform_bss(struct wiphy *wiphy,
        res->pub.tsf = timestamp;
        res->pub.beacon_interval = beacon_interval;
        res->pub.capability = capability;
-       /* point to after the private area */
-       res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
-       memcpy(res->pub.information_elements, ie, ielen);
-       res->pub.len_information_elements = ielen;
+       /*
+        * Since we do not know here whether the IEs are from a Beacon or Probe
+        * Response frame, we need to pick one of the options and only use it
+        * with the driver that does not provide the full Beacon/Probe Response
+        * frame. Use Beacon frame pointer to avoid indicating that this should
+        * override the information_elements pointer should we have received an
+        * earlier indication of Probe Response data.
+        *
+        * The initial buffer for the IEs is allocated with the BSS entry and
+        * is located after the private area.
+        */
+       res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
+       memcpy(res->pub.beacon_ies, ie, ielen);
+       res->pub.len_beacon_ies = ielen;
+       res->pub.information_elements = res->pub.beacon_ies;
+       res->pub.len_information_elements = res->pub.len_beacon_ies;
 
        kref_init(&res->ref);
 
-       res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0);
+       res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
        if (!res)
                return NULL;
 
@@ -517,10 +569,9 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        struct cfg80211_internal_bss *res;
        size_t ielen = len - offsetof(struct ieee80211_mgmt,
                                      u.probe_resp.variable);
-       bool overwrite;
        size_t privsz = wiphy->bss_priv_size;
 
-       if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
+       if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
                    (signal < 0 || signal > 100)))
                return NULL;
 
@@ -538,16 +589,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
        res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
-       /* point to after the private area */
-       res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
-       memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
-       res->pub.len_information_elements = ielen;
+       /*
+        * The initial buffer for the IEs is allocated with the BSS entry and
+        * is located after the private area.
+        */
+       if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+               res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
+               memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
+                      ielen);
+               res->pub.len_proberesp_ies = ielen;
+               res->pub.information_elements = res->pub.proberesp_ies;
+               res->pub.len_information_elements = res->pub.len_proberesp_ies;
+       } else {
+               res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
+               memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
+               res->pub.len_beacon_ies = ielen;
+               res->pub.information_elements = res->pub.beacon_ies;
+               res->pub.len_information_elements = res->pub.len_beacon_ies;
+       }
 
        kref_init(&res->ref);
 
-       overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
-
-       res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
+       res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
        if (!res)
                return NULL;
 
@@ -601,7 +664,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        struct cfg80211_registered_device *rdev;
        struct wiphy *wiphy;
        struct iw_scan_req *wreq = NULL;
-       struct cfg80211_scan_request *creq;
+       struct cfg80211_scan_request *creq = NULL;
        int i, err, n_channels = 0;
        enum ieee80211_band band;
 
@@ -694,8 +757,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        /* translate "Scan for SSID" request */
        if (wreq) {
                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
-                       if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
-                               return -EINVAL;
+                       if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
+                               err = -EINVAL;
+                               goto out;
+                       }
                        memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
                        creq->ssids[0].ssid_len = wreq->essid_len;
                }
@@ -707,12 +772,15 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        err = rdev->ops->scan(wiphy, dev, creq);
        if (err) {
                rdev->scan_req = NULL;
-               kfree(creq);
+               /* creq will be freed below */
        } else {
                nl80211_send_scan_start(rdev, dev);
+               /* creq now owned by driver */
+               creq = NULL;
                dev_hold(dev);
        }
  out:
+       kfree(creq);
        cfg80211_unlock_rdev(rdev);
        return err;
 }
@@ -874,35 +942,40 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                        cfg = ie + 2;
                        memset(&iwe, 0, sizeof(iwe));
                        iwe.cmd = IWEVCUSTOM;
-                       sprintf(buf, "Mesh network (version %d)", cfg[0]);
+                       sprintf(buf, "Mesh Network Path Selection Protocol ID: "
+                               "0x%02X", cfg[0]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       sprintf(buf, "Path Selection Metric ID: 0x%02X",
+                               cfg[1]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       sprintf(buf, "Congestion Control Mode ID: 0x%02X",
+                               cfg[2]);
                        iwe.u.data.length = strlen(buf);
                        current_ev = iwe_stream_add_point(info, current_ev,
                                                          end_buf,
                                                          &iwe, buf);
-                       sprintf(buf, "Path Selection Protocol ID: "
-                               "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
-                                                       cfg[4]);
+                       sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
                        iwe.u.data.length = strlen(buf);
                        current_ev = iwe_stream_add_point(info, current_ev,
                                                          end_buf,
                                                          &iwe, buf);
-                       sprintf(buf, "Path Selection Metric ID: "
-                               "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
-                                                       cfg[8]);
+                       sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
                        iwe.u.data.length = strlen(buf);
                        current_ev = iwe_stream_add_point(info, current_ev,
                                                          end_buf,
                                                          &iwe, buf);
-                       sprintf(buf, "Congestion Control Mode ID: "
-                               "0x%02X%02X%02X%02X", cfg[9], cfg[10],
-                                                       cfg[11], cfg[12]);
+                       sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
                        iwe.u.data.length = strlen(buf);
                        current_ev = iwe_stream_add_point(info, current_ev,
                                                          end_buf,
                                                          &iwe, buf);
-                       sprintf(buf, "Channel Precedence: "
-                               "0x%02X%02X%02X%02X", cfg[13], cfg[14],
-                                                       cfg[15], cfg[16]);
+                       sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
                        iwe.u.data.length = strlen(buf);
                        current_ev = iwe_stream_add_point(info, current_ev,
                                                          end_buf,
@@ -932,8 +1005,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
                ie += ie[1] + 2;
        }
 
-       if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
-           || ismesh) {
+       if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
+           ismesh) {
                memset(&iwe, 0, sizeof(iwe));
                iwe.cmd = SIOCGIWMODE;
                if (ismesh)