linux/net/wireless/scan.c
<<
>>
Prefs
   1/*
   2 * cfg80211 scan result handling
   3 *
   4 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
   5 */
   6#include <linux/kernel.h>
   7#include <linux/slab.h>
   8#include <linux/module.h>
   9#include <linux/netdevice.h>
  10#include <linux/wireless.h>
  11#include <linux/nl80211.h>
  12#include <linux/etherdevice.h>
  13#include <net/arp.h>
  14#include <net/cfg80211.h>
  15#include <net/iw_handler.h>
  16#include "core.h"
  17#include "nl80211.h"
  18#include "wext-compat.h"
  19
  20#define IEEE80211_SCAN_RESULT_EXPIRE    (15 * HZ)
  21
  22void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
  23{
  24        struct cfg80211_scan_request *request;
  25        struct net_device *dev;
  26#ifdef CONFIG_CFG80211_WEXT
  27        union iwreq_data wrqu;
  28#endif
  29
  30        ASSERT_RDEV_LOCK(rdev);
  31
  32        request = rdev->scan_req;
  33
  34        if (!request)
  35                return;
  36
  37        dev = request->dev;
  38
  39        /*
  40         * This must be before sending the other events!
  41         * Otherwise, wpa_supplicant gets completely confused with
  42         * wext events.
  43         */
  44        cfg80211_sme_scan_done(dev);
  45
  46        if (request->aborted)
  47                nl80211_send_scan_aborted(rdev, dev);
  48        else
  49                nl80211_send_scan_done(rdev, dev);
  50
  51#ifdef CONFIG_CFG80211_WEXT
  52        if (!request->aborted) {
  53                memset(&wrqu, 0, sizeof(wrqu));
  54
  55                wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
  56        }
  57#endif
  58
  59        dev_put(dev);
  60
  61        rdev->scan_req = NULL;
  62
  63        /*
  64         * OK. If this is invoked with "leak" then we can't
  65         * free this ... but we've cleaned it up anyway. The
  66         * driver failed to call the scan_done callback, so
  67         * all bets are off, it might still be trying to use
  68         * the scan request or not ... if it accesses the dev
  69         * in there (it shouldn't anyway) then it may crash.
  70         */
  71        if (!leak)
  72                kfree(request);
  73}
  74
  75void __cfg80211_scan_done(struct work_struct *wk)
  76{
  77        struct cfg80211_registered_device *rdev;
  78
  79        rdev = container_of(wk, struct cfg80211_registered_device,
  80                            scan_done_wk);
  81
  82        cfg80211_lock_rdev(rdev);
  83        ___cfg80211_scan_done(rdev, false);
  84        cfg80211_unlock_rdev(rdev);
  85}
  86
  87void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
  88{
  89        WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
  90
  91        request->aborted = aborted;
  92        queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
  93}
  94EXPORT_SYMBOL(cfg80211_scan_done);
  95
  96static void bss_release(struct kref *ref)
  97{
  98        struct cfg80211_internal_bss *bss;
  99
 100        bss = container_of(ref, struct cfg80211_internal_bss, ref);
 101        if (bss->pub.free_priv)
 102                bss->pub.free_priv(&bss->pub);
 103
 104        if (bss->beacon_ies_allocated)
 105                kfree(bss->pub.beacon_ies);
 106        if (bss->proberesp_ies_allocated)
 107                kfree(bss->pub.proberesp_ies);
 108
 109        BUG_ON(atomic_read(&bss->hold));
 110
 111        kfree(bss);
 112}
 113
 114/* must hold dev->bss_lock! */
 115void cfg80211_bss_age(struct cfg80211_registered_device *dev,
 116                      unsigned long age_secs)
 117{
 118        struct cfg80211_internal_bss *bss;
 119        unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
 120
 121        list_for_each_entry(bss, &dev->bss_list, list) {
 122                bss->ts -= age_jiffies;
 123        }
 124}
 125
 126/* must hold dev->bss_lock! */
 127void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
 128{
 129        struct cfg80211_internal_bss *bss, *tmp;
 130        bool expired = false;
 131
 132        list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
 133                if (atomic_read(&bss->hold))
 134                        continue;
 135                if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
 136                        continue;
 137                list_del(&bss->list);
 138                rb_erase(&bss->rbn, &dev->bss_tree);
 139                kref_put(&bss->ref, bss_release);
 140                expired = true;
 141        }
 142
 143        if (expired)
 144                dev->bss_generation++;
 145}
 146
 147const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
 148{
 149        while (len > 2 && ies[0] != eid) {
 150                len -= ies[1] + 2;
 151                ies += ies[1] + 2;
 152        }
 153        if (len < 2)
 154                return NULL;
 155        if (len < 2 + ies[1])
 156                return NULL;
 157        return ies;
 158}
 159EXPORT_SYMBOL(cfg80211_find_ie);
 160
 161static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
 162{
 163        const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
 164        const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
 165        int r;
 166
 167        if (!ie1 && !ie2)
 168                return 0;
 169        if (!ie1 || !ie2)
 170                return -1;
 171
 172        r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
 173        if (r == 0 && ie1[1] != ie2[1])
 174                return ie2[1] - ie1[1];
 175        return r;
 176}
 177
 178static bool is_bss(struct cfg80211_bss *a,
 179                   const u8 *bssid,
 180                   const u8 *ssid, size_t ssid_len)
 181{
 182        const u8 *ssidie;
 183
 184        if (bssid && compare_ether_addr(a->bssid, bssid))
 185                return false;
 186
 187        if (!ssid)
 188                return true;
 189
 190        ssidie = cfg80211_find_ie(WLAN_EID_SSID,
 191                                  a->information_elements,
 192                                  a->len_information_elements);
 193        if (!ssidie)
 194                return false;
 195        if (ssidie[1] != ssid_len)
 196                return false;
 197        return memcmp(ssidie + 2, ssid, ssid_len) == 0;
 198}
 199
 200static bool is_mesh(struct cfg80211_bss *a,
 201                    const u8 *meshid, size_t meshidlen,
 202                    const u8 *meshcfg)
 203{
 204        const u8 *ie;
 205
 206        if (!is_zero_ether_addr(a->bssid))
 207                return false;
 208
 209        ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
 210                              a->information_elements,
 211                              a->len_information_elements);
 212        if (!ie)
 213                return false;
 214        if (ie[1] != meshidlen)
 215                return false;
 216        if (memcmp(ie + 2, meshid, meshidlen))
 217                return false;
 218
 219        ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
 220                              a->information_elements,
 221                              a->len_information_elements);
 222        if (!ie)
 223                return false;
 224        if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
 225                return false;
 226
 227        /*
 228         * Ignore mesh capability (last two bytes of the IE) when
 229         * comparing since that may differ between stations taking
 230         * part in the same mesh.
 231         */
 232        return memcmp(ie + 2, meshcfg,
 233            sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
 234}
 235
 236static int cmp_bss(struct cfg80211_bss *a,
 237                   struct cfg80211_bss *b)
 238{
 239        int r;
 240
 241        if (a->channel != b->channel)
 242                return b->channel->center_freq - a->channel->center_freq;
 243
 244        r = memcmp(a->bssid, b->bssid, ETH_ALEN);
 245        if (r)
 246                return r;
 247
 248        if (is_zero_ether_addr(a->bssid)) {
 249                r = cmp_ies(WLAN_EID_MESH_ID,
 250                            a->information_elements,
 251                            a->len_information_elements,
 252                            b->information_elements,
 253                            b->len_information_elements);
 254                if (r)
 255                        return r;
 256                return cmp_ies(WLAN_EID_MESH_CONFIG,
 257                               a->information_elements,
 258                               a->len_information_elements,
 259                               b->information_elements,
 260                               b->len_information_elements);
 261        }
 262
 263        return cmp_ies(WLAN_EID_SSID,
 264                       a->information_elements,
 265                       a->len_information_elements,
 266                       b->information_elements,
 267                       b->len_information_elements);
 268}
 269
 270struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 271                                      struct ieee80211_channel *channel,
 272                                      const u8 *bssid,
 273                                      const u8 *ssid, size_t ssid_len,
 274                                      u16 capa_mask, u16 capa_val)
 275{
 276        struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
 277        struct cfg80211_internal_bss *bss, *res = NULL;
 278        unsigned long now = jiffies;
 279
 280        spin_lock_bh(&dev->bss_lock);
 281
 282        list_for_each_entry(bss, &dev->bss_list, list) {
 283                if ((bss->pub.capability & capa_mask) != capa_val)
 284                        continue;
 285                if (channel && bss->pub.channel != channel)
 286                        continue;
 287                /* Don't get expired BSS structs */
 288                if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
 289                    !atomic_read(&bss->hold))
 290                        continue;
 291                if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
 292                        res = bss;
 293                        kref_get(&res->ref);
 294                        break;
 295                }
 296        }
 297
 298        spin_unlock_bh(&dev->bss_lock);
 299        if (!res)
 300                return NULL;
 301        return &res->pub;
 302}
 303EXPORT_SYMBOL(cfg80211_get_bss);
 304
 305struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
 306                                       struct ieee80211_channel *channel,
 307                                       const u8 *meshid, size_t meshidlen,
 308                                       const u8 *meshcfg)
 309{
 310        struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
 311        struct cfg80211_internal_bss *bss, *res = NULL;
 312
 313        spin_lock_bh(&dev->bss_lock);
 314
 315        list_for_each_entry(bss, &dev->bss_list, list) {
 316                if (channel && bss->pub.channel != channel)
 317                        continue;
 318                if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
 319                        res = bss;
 320                        kref_get(&res->ref);
 321                        break;
 322                }
 323        }
 324
 325        spin_unlock_bh(&dev->bss_lock);
 326        if (!res)
 327                return NULL;
 328        return &res->pub;
 329}
 330EXPORT_SYMBOL(cfg80211_get_mesh);
 331
 332
 333static void rb_insert_bss(struct cfg80211_registered_device *dev,
 334                          struct cfg80211_internal_bss *bss)
 335{
 336        struct rb_node **p = &dev->bss_tree.rb_node;
 337        struct rb_node *parent = NULL;
 338        struct cfg80211_internal_bss *tbss;
 339        int cmp;
 340
 341        while (*p) {
 342                parent = *p;
 343                tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
 344
 345                cmp = cmp_bss(&bss->pub, &tbss->pub);
 346
 347                if (WARN_ON(!cmp)) {
 348                        /* will sort of leak this BSS */
 349                        return;
 350                }
 351
 352                if (cmp < 0)
 353                        p = &(*p)->rb_left;
 354                else
 355                        p = &(*p)->rb_right;
 356        }
 357
 358        rb_link_node(&bss->rbn, parent, p);
 359        rb_insert_color(&bss->rbn, &dev->bss_tree);
 360}
 361
 362static struct cfg80211_internal_bss *
 363rb_find_bss(struct cfg80211_registered_device *dev,
 364            struct cfg80211_internal_bss *res)
 365{
 366        struct rb_node *n = dev->bss_tree.rb_node;
 367        struct cfg80211_internal_bss *bss;
 368        int r;
 369
 370        while (n) {
 371                bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
 372                r = cmp_bss(&res->pub, &bss->pub);
 373
 374                if (r == 0)
 375                        return bss;
 376                else if (r < 0)
 377                        n = n->rb_left;
 378                else
 379                        n = n->rb_right;
 380        }
 381
 382        return NULL;
 383}
 384
 385static struct cfg80211_internal_bss *
 386cfg80211_bss_update(struct cfg80211_registered_device *dev,
 387                    struct cfg80211_internal_bss *res)
 388{
 389        struct cfg80211_internal_bss *found = NULL;
 390        const u8 *meshid, *meshcfg;
 391
 392        /*
 393         * The reference to "res" is donated to this function.
 394         */
 395
 396        if (WARN_ON(!res->pub.channel)) {
 397                kref_put(&res->ref, bss_release);
 398                return NULL;
 399        }
 400
 401        res->ts = jiffies;
 402
 403        if (is_zero_ether_addr(res->pub.bssid)) {
 404                /* must be mesh, verify */
 405                meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
 406                                          res->pub.information_elements,
 407                                          res->pub.len_information_elements);
 408                meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
 409                                           res->pub.information_elements,
 410                                           res->pub.len_information_elements);
 411                if (!meshid || !meshcfg ||
 412                    meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) {
 413                        /* bogus mesh */
 414                        kref_put(&res->ref, bss_release);
 415                        return NULL;
 416                }
 417        }
 418
 419        spin_lock_bh(&dev->bss_lock);
 420
 421        found = rb_find_bss(dev, res);
 422
 423        if (found) {
 424                found->pub.beacon_interval = res->pub.beacon_interval;
 425                found->pub.tsf = res->pub.tsf;
 426                found->pub.signal = res->pub.signal;
 427                found->pub.capability = res->pub.capability;
 428                found->ts = res->ts;
 429
 430                /* Update IEs */
 431                if (res->pub.proberesp_ies) {
 432                        size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
 433                        size_t ielen = res->pub.len_proberesp_ies;
 434
 435                        if (found->pub.proberesp_ies &&
 436                            !found->proberesp_ies_allocated &&
 437                            ksize(found) >= used + ielen) {
 438                                memcpy(found->pub.proberesp_ies,
 439                                       res->pub.proberesp_ies, ielen);
 440                                found->pub.len_proberesp_ies = ielen;
 441                        } else {
 442                                u8 *ies = found->pub.proberesp_ies;
 443
 444                                if (found->proberesp_ies_allocated)
 445                                        ies = krealloc(ies, ielen, GFP_ATOMIC);
 446                                else
 447                                        ies = kmalloc(ielen, GFP_ATOMIC);
 448
 449                                if (ies) {
 450                                        memcpy(ies, res->pub.proberesp_ies,
 451                                               ielen);
 452                                        found->proberesp_ies_allocated = true;
 453                                        found->pub.proberesp_ies = ies;
 454                                        found->pub.len_proberesp_ies = ielen;
 455                                }
 456                        }
 457
 458                        /* Override possible earlier Beacon frame IEs */
 459                        found->pub.information_elements =
 460                                found->pub.proberesp_ies;
 461                        found->pub.len_information_elements =
 462                                found->pub.len_proberesp_ies;
 463                }
 464                if (res->pub.beacon_ies) {
 465                        size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
 466                        size_t ielen = res->pub.len_beacon_ies;
 467
 468                        if (found->pub.beacon_ies &&
 469                            !found->beacon_ies_allocated &&
 470                            ksize(found) >= used + ielen) {
 471                                memcpy(found->pub.beacon_ies,
 472                                       res->pub.beacon_ies, ielen);
 473                                found->pub.len_beacon_ies = ielen;
 474                        } else {
 475                                u8 *ies = found->pub.beacon_ies;
 476
 477                                if (found->beacon_ies_allocated)
 478                                        ies = krealloc(ies, ielen, GFP_ATOMIC);
 479                                else
 480                                        ies = kmalloc(ielen, GFP_ATOMIC);
 481
 482                                if (ies) {
 483                                        memcpy(ies, res->pub.beacon_ies,
 484                                               ielen);
 485                                        found->beacon_ies_allocated = true;
 486                                        found->pub.beacon_ies = ies;
 487                                        found->pub.len_beacon_ies = ielen;
 488                                }
 489                        }
 490                }
 491
 492                kref_put(&res->ref, bss_release);
 493        } else {
 494                /* this "consumes" the reference */
 495                list_add_tail(&res->list, &dev->bss_list);
 496                rb_insert_bss(dev, res);
 497                found = res;
 498        }
 499
 500        dev->bss_generation++;
 501        spin_unlock_bh(&dev->bss_lock);
 502
 503        kref_get(&found->ref);
 504        return found;
 505}
 506
 507struct cfg80211_bss*
 508cfg80211_inform_bss(struct wiphy *wiphy,
 509                    struct ieee80211_channel *channel,
 510                    const u8 *bssid,
 511                    u64 timestamp, u16 capability, u16 beacon_interval,
 512                    const u8 *ie, size_t ielen,
 513                    s32 signal, gfp_t gfp)
 514{
 515        struct cfg80211_internal_bss *res;
 516        size_t privsz;
 517
 518        if (WARN_ON(!wiphy))
 519                return NULL;
 520
 521        privsz = wiphy->bss_priv_size;
 522
 523        if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
 524                        (signal < 0 || signal > 100)))
 525                return NULL;
 526
 527        res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
 528        if (!res)
 529                return NULL;
 530
 531        memcpy(res->pub.bssid, bssid, ETH_ALEN);
 532        res->pub.channel = channel;
 533        res->pub.signal = signal;
 534        res->pub.tsf = timestamp;
 535        res->pub.beacon_interval = beacon_interval;
 536        res->pub.capability = capability;
 537        /*
 538         * Since we do not know here whether the IEs are from a Beacon or Probe
 539         * Response frame, we need to pick one of the options and only use it
 540         * with the driver that does not provide the full Beacon/Probe Response
 541         * frame. Use Beacon frame pointer to avoid indicating that this should
 542         * override the information_elements pointer should we have received an
 543         * earlier indication of Probe Response data.
 544         *
 545         * The initial buffer for the IEs is allocated with the BSS entry and
 546         * is located after the private area.
 547         */
 548        res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
 549        memcpy(res->pub.beacon_ies, ie, ielen);
 550        res->pub.len_beacon_ies = ielen;
 551        res->pub.information_elements = res->pub.beacon_ies;
 552        res->pub.len_information_elements = res->pub.len_beacon_ies;
 553
 554        kref_init(&res->ref);
 555
 556        res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
 557        if (!res)
 558                return NULL;
 559
 560        if (res->pub.capability & WLAN_CAPABILITY_ESS)
 561                regulatory_hint_found_beacon(wiphy, channel, gfp);
 562
 563        /* cfg80211_bss_update gives us a referenced result */
 564        return &res->pub;
 565}
 566EXPORT_SYMBOL(cfg80211_inform_bss);
 567
 568struct cfg80211_bss *
 569cfg80211_inform_bss_frame(struct wiphy *wiphy,
 570                          struct ieee80211_channel *channel,
 571                          struct ieee80211_mgmt *mgmt, size_t len,
 572                          s32 signal, gfp_t gfp)
 573{
 574        struct cfg80211_internal_bss *res;
 575        size_t ielen = len - offsetof(struct ieee80211_mgmt,
 576                                      u.probe_resp.variable);
 577        size_t privsz = wiphy->bss_priv_size;
 578
 579        if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
 580                    (signal < 0 || signal > 100)))
 581                return NULL;
 582
 583        if (WARN_ON(!mgmt || !wiphy ||
 584                    len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
 585                return NULL;
 586
 587        res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
 588        if (!res)
 589                return NULL;
 590
 591        memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
 592        res->pub.channel = channel;
 593        res->pub.signal = signal;
 594        res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
 595        res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
 596        res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
 597        /*
 598         * The initial buffer for the IEs is allocated with the BSS entry and
 599         * is located after the private area.
 600         */
 601        if (ieee80211_is_probe_resp(mgmt->frame_control)) {
 602                res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
 603                memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
 604                       ielen);
 605                res->pub.len_proberesp_ies = ielen;
 606                res->pub.information_elements = res->pub.proberesp_ies;
 607                res->pub.len_information_elements = res->pub.len_proberesp_ies;
 608        } else {
 609                res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
 610                memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
 611                res->pub.len_beacon_ies = ielen;
 612                res->pub.information_elements = res->pub.beacon_ies;
 613                res->pub.len_information_elements = res->pub.len_beacon_ies;
 614        }
 615
 616        kref_init(&res->ref);
 617
 618        res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
 619        if (!res)
 620                return NULL;
 621
 622        if (res->pub.capability & WLAN_CAPABILITY_ESS)
 623                regulatory_hint_found_beacon(wiphy, channel, gfp);
 624
 625        /* cfg80211_bss_update gives us a referenced result */
 626        return &res->pub;
 627}
 628EXPORT_SYMBOL(cfg80211_inform_bss_frame);
 629
 630void cfg80211_put_bss(struct cfg80211_bss *pub)
 631{
 632        struct cfg80211_internal_bss *bss;
 633
 634        if (!pub)
 635                return;
 636
 637        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 638        kref_put(&bss->ref, bss_release);
 639}
 640EXPORT_SYMBOL(cfg80211_put_bss);
 641
 642void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 643{
 644        struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
 645        struct cfg80211_internal_bss *bss;
 646
 647        if (WARN_ON(!pub))
 648                return;
 649
 650        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 651
 652        spin_lock_bh(&dev->bss_lock);
 653        if (!list_empty(&bss->list)) {
 654                list_del_init(&bss->list);
 655                dev->bss_generation++;
 656                rb_erase(&bss->rbn, &dev->bss_tree);
 657
 658                kref_put(&bss->ref, bss_release);
 659        }
 660        spin_unlock_bh(&dev->bss_lock);
 661}
 662EXPORT_SYMBOL(cfg80211_unlink_bss);
 663
 664#ifdef CONFIG_CFG80211_WEXT
 665int cfg80211_wext_siwscan(struct net_device *dev,
 666                          struct iw_request_info *info,
 667                          union iwreq_data *wrqu, char *extra)
 668{
 669        struct cfg80211_registered_device *rdev;
 670        struct wiphy *wiphy;
 671        struct iw_scan_req *wreq = NULL;
 672        struct cfg80211_scan_request *creq = NULL;
 673        int i, err, n_channels = 0;
 674        enum ieee80211_band band;
 675
 676        if (!netif_running(dev))
 677                return -ENETDOWN;
 678
 679        if (wrqu->data.length == sizeof(struct iw_scan_req))
 680                wreq = (struct iw_scan_req *)extra;
 681
 682        rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
 683
 684        if (IS_ERR(rdev))
 685                return PTR_ERR(rdev);
 686
 687        if (rdev->scan_req) {
 688                err = -EBUSY;
 689                goto out;
 690        }
 691
 692        wiphy = &rdev->wiphy;
 693
 694        /* Determine number of channels, needed to allocate creq */
 695        if (wreq && wreq->num_channels)
 696                n_channels = wreq->num_channels;
 697        else {
 698                for (band = 0; band < IEEE80211_NUM_BANDS; band++)
 699                        if (wiphy->bands[band])
 700                                n_channels += wiphy->bands[band]->n_channels;
 701        }
 702
 703        creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
 704                       n_channels * sizeof(void *),
 705                       GFP_ATOMIC);
 706        if (!creq) {
 707                err = -ENOMEM;
 708                goto out;
 709        }
 710
 711        creq->wiphy = wiphy;
 712        creq->dev = dev;
 713        /* SSIDs come after channels */
 714        creq->ssids = (void *)&creq->channels[n_channels];
 715        creq->n_channels = n_channels;
 716        creq->n_ssids = 1;
 717
 718        /* translate "Scan on frequencies" request */
 719        i = 0;
 720        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
 721                int j;
 722
 723                if (!wiphy->bands[band])
 724                        continue;
 725
 726                for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
 727                        /* ignore disabled channels */
 728                        if (wiphy->bands[band]->channels[j].flags &
 729                                                IEEE80211_CHAN_DISABLED)
 730                                continue;
 731
 732                        /* If we have a wireless request structure and the
 733                         * wireless request specifies frequencies, then search
 734                         * for the matching hardware channel.
 735                         */
 736                        if (wreq && wreq->num_channels) {
 737                                int k;
 738                                int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
 739                                for (k = 0; k < wreq->num_channels; k++) {
 740                                        int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]);
 741                                        if (wext_freq == wiphy_freq)
 742                                                goto wext_freq_found;
 743                                }
 744                                goto wext_freq_not_found;
 745                        }
 746
 747                wext_freq_found:
 748                        creq->channels[i] = &wiphy->bands[band]->channels[j];
 749                        i++;
 750                wext_freq_not_found: ;
 751                }
 752        }
 753        /* No channels found? */
 754        if (!i) {
 755                err = -EINVAL;
 756                goto out;
 757        }
 758
 759        /* Set real number of channels specified in creq->channels[] */
 760        creq->n_channels = i;
 761
 762        /* translate "Scan for SSID" request */
 763        if (wreq) {
 764                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
 765                        if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
 766                                err = -EINVAL;
 767                                goto out;
 768                        }
 769                        memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
 770                        creq->ssids[0].ssid_len = wreq->essid_len;
 771                }
 772                if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
 773                        creq->n_ssids = 0;
 774        }
 775
 776        rdev->scan_req = creq;
 777        err = rdev->ops->scan(wiphy, dev, creq);
 778        if (err) {
 779                rdev->scan_req = NULL;
 780                /* creq will be freed below */
 781        } else {
 782                nl80211_send_scan_start(rdev, dev);
 783                /* creq now owned by driver */
 784                creq = NULL;
 785                dev_hold(dev);
 786        }
 787 out:
 788        kfree(creq);
 789        cfg80211_unlock_rdev(rdev);
 790        return err;
 791}
 792EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
 793
 794static void ieee80211_scan_add_ies(struct iw_request_info *info,
 795                                   struct cfg80211_bss *bss,
 796                                   char **current_ev, char *end_buf)
 797{
 798        u8 *pos, *end, *next;
 799        struct iw_event iwe;
 800
 801        if (!bss->information_elements ||
 802            !bss->len_information_elements)
 803                return;
 804
 805        /*
 806         * If needed, fragment the IEs buffer (at IE boundaries) into short
 807         * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
 808         */
 809        pos = bss->information_elements;
 810        end = pos + bss->len_information_elements;
 811
 812        while (end - pos > IW_GENERIC_IE_MAX) {
 813                next = pos + 2 + pos[1];
 814                while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
 815                        next = next + 2 + next[1];
 816
 817                memset(&iwe, 0, sizeof(iwe));
 818                iwe.cmd = IWEVGENIE;
 819                iwe.u.data.length = next - pos;
 820                *current_ev = iwe_stream_add_point(info, *current_ev,
 821                                                   end_buf, &iwe, pos);
 822
 823                pos = next;
 824        }
 825
 826        if (end > pos) {
 827                memset(&iwe, 0, sizeof(iwe));
 828                iwe.cmd = IWEVGENIE;
 829                iwe.u.data.length = end - pos;
 830                *current_ev = iwe_stream_add_point(info, *current_ev,
 831                                                   end_buf, &iwe, pos);
 832        }
 833}
 834
 835static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 836{
 837        unsigned long end = jiffies;
 838
 839        if (end >= start)
 840                return jiffies_to_msecs(end - start);
 841
 842        return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
 843}
 844
 845static char *
 846ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
 847              struct cfg80211_internal_bss *bss, char *current_ev,
 848              char *end_buf)
 849{
 850        struct iw_event iwe;
 851        u8 *buf, *cfg, *p;
 852        u8 *ie = bss->pub.information_elements;
 853        int rem = bss->pub.len_information_elements, i, sig;
 854        bool ismesh = false;
 855
 856        memset(&iwe, 0, sizeof(iwe));
 857        iwe.cmd = SIOCGIWAP;
 858        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
 859        memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
 860        current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 861                                          IW_EV_ADDR_LEN);
 862
 863        memset(&iwe, 0, sizeof(iwe));
 864        iwe.cmd = SIOCGIWFREQ;
 865        iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
 866        iwe.u.freq.e = 0;
 867        current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 868                                          IW_EV_FREQ_LEN);
 869
 870        memset(&iwe, 0, sizeof(iwe));
 871        iwe.cmd = SIOCGIWFREQ;
 872        iwe.u.freq.m = bss->pub.channel->center_freq;
 873        iwe.u.freq.e = 6;
 874        current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
 875                                          IW_EV_FREQ_LEN);
 876
 877        if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
 878                memset(&iwe, 0, sizeof(iwe));
 879                iwe.cmd = IWEVQUAL;
 880                iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
 881                                     IW_QUAL_NOISE_INVALID |
 882                                     IW_QUAL_QUAL_UPDATED;
 883                switch (wiphy->signal_type) {
 884                case CFG80211_SIGNAL_TYPE_MBM:
 885                        sig = bss->pub.signal / 100;
 886                        iwe.u.qual.level = sig;
 887                        iwe.u.qual.updated |= IW_QUAL_DBM;
 888                        if (sig < -110)         /* rather bad */
 889                                sig = -110;
 890                        else if (sig > -40)     /* perfect */
 891                                sig = -40;
 892                        /* will give a range of 0 .. 70 */
 893                        iwe.u.qual.qual = sig + 110;
 894                        break;
 895                case CFG80211_SIGNAL_TYPE_UNSPEC:
 896                        iwe.u.qual.level = bss->pub.signal;
 897                        /* will give range 0 .. 100 */
 898                        iwe.u.qual.qual = bss->pub.signal;
 899                        break;
 900                default:
 901                        /* not reached */
 902                        break;
 903                }
 904                current_ev = iwe_stream_add_event(info, current_ev, end_buf,
 905                                                  &iwe, IW_EV_QUAL_LEN);
 906        }
 907
 908        memset(&iwe, 0, sizeof(iwe));
 909        iwe.cmd = SIOCGIWENCODE;
 910        if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
 911                iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
 912        else
 913                iwe.u.data.flags = IW_ENCODE_DISABLED;
 914        iwe.u.data.length = 0;
 915        current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 916                                          &iwe, "");
 917
 918        while (rem >= 2) {
 919                /* invalid data */
 920                if (ie[1] > rem - 2)
 921                        break;
 922
 923                switch (ie[0]) {
 924                case WLAN_EID_SSID:
 925                        memset(&iwe, 0, sizeof(iwe));
 926                        iwe.cmd = SIOCGIWESSID;
 927                        iwe.u.data.length = ie[1];
 928                        iwe.u.data.flags = 1;
 929                        current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 930                                                          &iwe, ie + 2);
 931                        break;
 932                case WLAN_EID_MESH_ID:
 933                        memset(&iwe, 0, sizeof(iwe));
 934                        iwe.cmd = SIOCGIWESSID;
 935                        iwe.u.data.length = ie[1];
 936                        iwe.u.data.flags = 1;
 937                        current_ev = iwe_stream_add_point(info, current_ev, end_buf,
 938                                                          &iwe, ie + 2);
 939                        break;
 940                case WLAN_EID_MESH_CONFIG:
 941                        ismesh = true;
 942                        if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
 943                                break;
 944                        buf = kmalloc(50, GFP_ATOMIC);
 945                        if (!buf)
 946                                break;
 947                        cfg = ie + 2;
 948                        memset(&iwe, 0, sizeof(iwe));
 949                        iwe.cmd = IWEVCUSTOM;
 950                        sprintf(buf, "Mesh Network Path Selection Protocol ID: "
 951                                "0x%02X", cfg[0]);
 952                        iwe.u.data.length = strlen(buf);
 953                        current_ev = iwe_stream_add_point(info, current_ev,
 954                                                          end_buf,
 955                                                          &iwe, buf);
 956                        sprintf(buf, "Path Selection Metric ID: 0x%02X",
 957                                cfg[1]);
 958                        iwe.u.data.length = strlen(buf);
 959                        current_ev = iwe_stream_add_point(info, current_ev,
 960                                                          end_buf,
 961                                                          &iwe, buf);
 962                        sprintf(buf, "Congestion Control Mode ID: 0x%02X",
 963                                cfg[2]);
 964                        iwe.u.data.length = strlen(buf);
 965                        current_ev = iwe_stream_add_point(info, current_ev,
 966                                                          end_buf,
 967                                                          &iwe, buf);
 968                        sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
 969                        iwe.u.data.length = strlen(buf);
 970                        current_ev = iwe_stream_add_point(info, current_ev,
 971                                                          end_buf,
 972                                                          &iwe, buf);
 973                        sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
 974                        iwe.u.data.length = strlen(buf);
 975                        current_ev = iwe_stream_add_point(info, current_ev,
 976                                                          end_buf,
 977                                                          &iwe, buf);
 978                        sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
 979                        iwe.u.data.length = strlen(buf);
 980                        current_ev = iwe_stream_add_point(info, current_ev,
 981                                                          end_buf,
 982                                                          &iwe, buf);
 983                        sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
 984                        iwe.u.data.length = strlen(buf);
 985                        current_ev = iwe_stream_add_point(info, current_ev,
 986                                                          end_buf,
 987                                                          &iwe, buf);
 988                        kfree(buf);
 989                        break;
 990                case WLAN_EID_SUPP_RATES:
 991                case WLAN_EID_EXT_SUPP_RATES:
 992                        /* display all supported rates in readable format */
 993                        p = current_ev + iwe_stream_lcp_len(info);
 994
 995                        memset(&iwe, 0, sizeof(iwe));
 996                        iwe.cmd = SIOCGIWRATE;
 997                        /* Those two flags are ignored... */
 998                        iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
 999
1000                        for (i = 0; i < ie[1]; i++) {
1001                                iwe.u.bitrate.value =
1002                                        ((ie[i + 2] & 0x7f) * 500000);
1003                                p = iwe_stream_add_value(info, current_ev, p,
1004                                                end_buf, &iwe, IW_EV_PARAM_LEN);
1005                        }
1006                        current_ev = p;
1007                        break;
1008                }
1009                rem -= ie[1] + 2;
1010                ie += ie[1] + 2;
1011        }
1012
1013        if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
1014            ismesh) {
1015                memset(&iwe, 0, sizeof(iwe));
1016                iwe.cmd = SIOCGIWMODE;
1017                if (ismesh)
1018                        iwe.u.mode = IW_MODE_MESH;
1019                else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
1020                        iwe.u.mode = IW_MODE_MASTER;
1021                else
1022                        iwe.u.mode = IW_MODE_ADHOC;
1023                current_ev = iwe_stream_add_event(info, current_ev, end_buf,
1024                                                  &iwe, IW_EV_UINT_LEN);
1025        }
1026
1027        buf = kmalloc(30, GFP_ATOMIC);
1028        if (buf) {
1029                memset(&iwe, 0, sizeof(iwe));
1030                iwe.cmd = IWEVCUSTOM;
1031                sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
1032                iwe.u.data.length = strlen(buf);
1033                current_ev = iwe_stream_add_point(info, current_ev, end_buf,
1034                                                  &iwe, buf);
1035                memset(&iwe, 0, sizeof(iwe));
1036                iwe.cmd = IWEVCUSTOM;
1037                sprintf(buf, " Last beacon: %ums ago",
1038                        elapsed_jiffies_msecs(bss->ts));
1039                iwe.u.data.length = strlen(buf);
1040                current_ev = iwe_stream_add_point(info, current_ev,
1041                                                  end_buf, &iwe, buf);
1042                kfree(buf);
1043        }
1044
1045        ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf);
1046
1047        return current_ev;
1048}
1049
1050
1051static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
1052                                  struct iw_request_info *info,
1053                                  char *buf, size_t len)
1054{
1055        char *current_ev = buf;
1056        char *end_buf = buf + len;
1057        struct cfg80211_internal_bss *bss;
1058
1059        spin_lock_bh(&dev->bss_lock);
1060        cfg80211_bss_expire(dev);
1061
1062        list_for_each_entry(bss, &dev->bss_list, list) {
1063                if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
1064                        spin_unlock_bh(&dev->bss_lock);
1065                        return -E2BIG;
1066                }
1067                current_ev = ieee80211_bss(&dev->wiphy, info, bss,
1068                                           current_ev, end_buf);
1069        }
1070        spin_unlock_bh(&dev->bss_lock);
1071        return current_ev - buf;
1072}
1073
1074
1075int cfg80211_wext_giwscan(struct net_device *dev,
1076                          struct iw_request_info *info,
1077                          struct iw_point *data, char *extra)
1078{
1079        struct cfg80211_registered_device *rdev;
1080        int res;
1081
1082        if (!netif_running(dev))
1083                return -ENETDOWN;
1084
1085        rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
1086
1087        if (IS_ERR(rdev))
1088                return PTR_ERR(rdev);
1089
1090        if (rdev->scan_req) {
1091                res = -EAGAIN;
1092                goto out;
1093        }
1094
1095        res = ieee80211_scan_results(rdev, info, extra, data->length);
1096        data->length = 0;
1097        if (res >= 0) {
1098                data->length = res;
1099                res = 0;
1100        }
1101
1102 out:
1103        cfg80211_unlock_rdev(rdev);
1104        return res;
1105}
1106EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
1107#endif
1108