linux/net/wireless/wext-sme.c
<<
>>
Prefs
   1/*
   2 * cfg80211 wext compat for managed mode.
   3 *
   4 * Copyright 2009       Johannes Berg <johannes@sipsolutions.net>
   5 * Copyright (C) 2009   Intel Corporation. All rights reserved.
   6 */
   7
   8#include <linux/export.h>
   9#include <linux/etherdevice.h>
  10#include <linux/if_arp.h>
  11#include <linux/slab.h>
  12#include <net/cfg80211.h>
  13#include <net/cfg80211-wext.h>
  14#include "wext-compat.h"
  15#include "nl80211.h"
  16
  17int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
  18                              struct wireless_dev *wdev)
  19{
  20        struct cfg80211_cached_keys *ck = NULL;
  21        const u8 *prev_bssid = NULL;
  22        int err, i;
  23
  24        ASSERT_RDEV_LOCK(rdev);
  25        ASSERT_WDEV_LOCK(wdev);
  26
  27        if (!netif_running(wdev->netdev))
  28                return 0;
  29
  30        wdev->wext.connect.ie = wdev->wext.ie;
  31        wdev->wext.connect.ie_len = wdev->wext.ie_len;
  32
  33        /* Use default background scan period */
  34        wdev->wext.connect.bg_scan_period = -1;
  35
  36        if (wdev->wext.keys) {
  37                wdev->wext.keys->def = wdev->wext.default_key;
  38                wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
  39                if (wdev->wext.default_key != -1)
  40                        wdev->wext.connect.privacy = true;
  41        }
  42
  43        if (!wdev->wext.connect.ssid_len)
  44                return 0;
  45
  46        if (wdev->wext.keys) {
  47                ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
  48                if (!ck)
  49                        return -ENOMEM;
  50                for (i = 0; i < 6; i++)
  51                        ck->params[i].key = ck->data[i];
  52        }
  53
  54        if (wdev->wext.prev_bssid_valid)
  55                prev_bssid = wdev->wext.prev_bssid;
  56
  57        err = __cfg80211_connect(rdev, wdev->netdev,
  58                                 &wdev->wext.connect, ck, prev_bssid);
  59        if (err)
  60                kfree(ck);
  61
  62        return err;
  63}
  64
  65int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
  66                              struct iw_request_info *info,
  67                              struct iw_freq *wextfreq, char *extra)
  68{
  69        struct wireless_dev *wdev = dev->ieee80211_ptr;
  70        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
  71        struct ieee80211_channel *chan = NULL;
  72        int err, freq;
  73
  74        /* call only for station! */
  75        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  76                return -EINVAL;
  77
  78        freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
  79        if (freq < 0)
  80                return freq;
  81
  82        if (freq) {
  83                chan = ieee80211_get_channel(wdev->wiphy, freq);
  84                if (!chan)
  85                        return -EINVAL;
  86                if (chan->flags & IEEE80211_CHAN_DISABLED)
  87                        return -EINVAL;
  88        }
  89
  90        cfg80211_lock_rdev(rdev);
  91        mutex_lock(&rdev->devlist_mtx);
  92        wdev_lock(wdev);
  93
  94        if (wdev->sme_state != CFG80211_SME_IDLE) {
  95                bool event = true;
  96
  97                if (wdev->wext.connect.channel == chan) {
  98                        err = 0;
  99                        goto out;
 100                }
 101
 102                /* if SSID set, we'll try right again, avoid event */
 103                if (wdev->wext.connect.ssid_len)
 104                        event = false;
 105                err = __cfg80211_disconnect(rdev, dev,
 106                                            WLAN_REASON_DEAUTH_LEAVING, event);
 107                if (err)
 108                        goto out;
 109        }
 110
 111
 112        wdev->wext.connect.channel = chan;
 113
 114        /*
 115         * SSID is not set, we just want to switch monitor channel,
 116         * this is really just backward compatibility, if the SSID
 117         * is set then we use the channel to select the BSS to use
 118         * to connect to instead. If we were connected on another
 119         * channel we disconnected above and reconnect below.
 120         */
 121        if (chan && !wdev->wext.connect.ssid_len) {
 122                struct cfg80211_chan_def chandef = {
 123                        .width = NL80211_CHAN_WIDTH_20_NOHT,
 124                        .center_freq1 = freq,
 125                };
 126
 127                chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
 128                if (chandef.chan)
 129                        err = cfg80211_set_monitor_channel(rdev, &chandef);
 130                else
 131                        err = -EINVAL;
 132                goto out;
 133        }
 134
 135        err = cfg80211_mgd_wext_connect(rdev, wdev);
 136 out:
 137        wdev_unlock(wdev);
 138        mutex_unlock(&rdev->devlist_mtx);
 139        cfg80211_unlock_rdev(rdev);
 140        return err;
 141}
 142
 143int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
 144                              struct iw_request_info *info,
 145                              struct iw_freq *freq, char *extra)
 146{
 147        struct wireless_dev *wdev = dev->ieee80211_ptr;
 148        struct ieee80211_channel *chan = NULL;
 149
 150        /* call only for station! */
 151        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 152                return -EINVAL;
 153
 154        wdev_lock(wdev);
 155        if (wdev->current_bss)
 156                chan = wdev->current_bss->pub.channel;
 157        else if (wdev->wext.connect.channel)
 158                chan = wdev->wext.connect.channel;
 159        wdev_unlock(wdev);
 160
 161        if (chan) {
 162                freq->m = chan->center_freq;
 163                freq->e = 6;
 164                return 0;
 165        }
 166
 167        /* no channel if not joining */
 168        return -EINVAL;
 169}
 170
 171int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 172                               struct iw_request_info *info,
 173                               struct iw_point *data, char *ssid)
 174{
 175        struct wireless_dev *wdev = dev->ieee80211_ptr;
 176        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 177        size_t len = data->length;
 178        int err;
 179
 180        /* call only for station! */
 181        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 182                return -EINVAL;
 183
 184        if (!data->flags)
 185                len = 0;
 186
 187        /* iwconfig uses nul termination in SSID.. */
 188        if (len > 0 && ssid[len - 1] == '\0')
 189                len--;
 190
 191        cfg80211_lock_rdev(rdev);
 192        mutex_lock(&rdev->devlist_mtx);
 193        wdev_lock(wdev);
 194
 195        err = 0;
 196
 197        if (wdev->sme_state != CFG80211_SME_IDLE) {
 198                bool event = true;
 199
 200                if (wdev->wext.connect.ssid && len &&
 201                    len == wdev->wext.connect.ssid_len &&
 202                    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
 203                        goto out;
 204
 205                /* if SSID set now, we'll try to connect, avoid event */
 206                if (len)
 207                        event = false;
 208                err = __cfg80211_disconnect(rdev, dev,
 209                                            WLAN_REASON_DEAUTH_LEAVING, event);
 210                if (err)
 211                        goto out;
 212        }
 213
 214        wdev->wext.prev_bssid_valid = false;
 215        wdev->wext.connect.ssid = wdev->wext.ssid;
 216        memcpy(wdev->wext.ssid, ssid, len);
 217        wdev->wext.connect.ssid_len = len;
 218
 219        wdev->wext.connect.crypto.control_port = false;
 220        wdev->wext.connect.crypto.control_port_ethertype =
 221                                        cpu_to_be16(ETH_P_PAE);
 222
 223        err = cfg80211_mgd_wext_connect(rdev, wdev);
 224 out:
 225        wdev_unlock(wdev);
 226        mutex_unlock(&rdev->devlist_mtx);
 227        cfg80211_unlock_rdev(rdev);
 228        return err;
 229}
 230
 231int cfg80211_mgd_wext_giwessid(struct net_device *dev,
 232                               struct iw_request_info *info,
 233                               struct iw_point *data, char *ssid)
 234{
 235        struct wireless_dev *wdev = dev->ieee80211_ptr;
 236
 237        /* call only for station! */
 238        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 239                return -EINVAL;
 240
 241        data->flags = 0;
 242
 243        wdev_lock(wdev);
 244        if (wdev->current_bss) {
 245                const u8 *ie;
 246
 247                rcu_read_lock();
 248                ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
 249                                          WLAN_EID_SSID);
 250                if (ie) {
 251                        data->flags = 1;
 252                        data->length = ie[1];
 253                        memcpy(ssid, ie + 2, data->length);
 254                }
 255                rcu_read_unlock();
 256        } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
 257                data->flags = 1;
 258                data->length = wdev->wext.connect.ssid_len;
 259                memcpy(ssid, wdev->wext.connect.ssid, data->length);
 260        }
 261        wdev_unlock(wdev);
 262
 263        return 0;
 264}
 265
 266int cfg80211_mgd_wext_siwap(struct net_device *dev,
 267                            struct iw_request_info *info,
 268                            struct sockaddr *ap_addr, char *extra)
 269{
 270        struct wireless_dev *wdev = dev->ieee80211_ptr;
 271        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 272        u8 *bssid = ap_addr->sa_data;
 273        int err;
 274
 275        /* call only for station! */
 276        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 277                return -EINVAL;
 278
 279        if (ap_addr->sa_family != ARPHRD_ETHER)
 280                return -EINVAL;
 281
 282        /* automatic mode */
 283        if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
 284                bssid = NULL;
 285
 286        cfg80211_lock_rdev(rdev);
 287        mutex_lock(&rdev->devlist_mtx);
 288        wdev_lock(wdev);
 289
 290        if (wdev->sme_state != CFG80211_SME_IDLE) {
 291                err = 0;
 292                /* both automatic */
 293                if (!bssid && !wdev->wext.connect.bssid)
 294                        goto out;
 295
 296                /* fixed already - and no change */
 297                if (wdev->wext.connect.bssid && bssid &&
 298                    ether_addr_equal(bssid, wdev->wext.connect.bssid))
 299                        goto out;
 300
 301                err = __cfg80211_disconnect(rdev, dev,
 302                                            WLAN_REASON_DEAUTH_LEAVING, false);
 303                if (err)
 304                        goto out;
 305        }
 306
 307        if (bssid) {
 308                memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
 309                wdev->wext.connect.bssid = wdev->wext.bssid;
 310        } else
 311                wdev->wext.connect.bssid = NULL;
 312
 313        err = cfg80211_mgd_wext_connect(rdev, wdev);
 314 out:
 315        wdev_unlock(wdev);
 316        mutex_unlock(&rdev->devlist_mtx);
 317        cfg80211_unlock_rdev(rdev);
 318        return err;
 319}
 320
 321int cfg80211_mgd_wext_giwap(struct net_device *dev,
 322                            struct iw_request_info *info,
 323                            struct sockaddr *ap_addr, char *extra)
 324{
 325        struct wireless_dev *wdev = dev->ieee80211_ptr;
 326
 327        /* call only for station! */
 328        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 329                return -EINVAL;
 330
 331        ap_addr->sa_family = ARPHRD_ETHER;
 332
 333        wdev_lock(wdev);
 334        if (wdev->current_bss)
 335                memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
 336        else
 337                memset(ap_addr->sa_data, 0, ETH_ALEN);
 338        wdev_unlock(wdev);
 339
 340        return 0;
 341}
 342
 343int cfg80211_wext_siwgenie(struct net_device *dev,
 344                           struct iw_request_info *info,
 345                           struct iw_point *data, char *extra)
 346{
 347        struct wireless_dev *wdev = dev->ieee80211_ptr;
 348        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 349        u8 *ie = extra;
 350        int ie_len = data->length, err;
 351
 352        if (wdev->iftype != NL80211_IFTYPE_STATION)
 353                return -EOPNOTSUPP;
 354
 355        if (!ie_len)
 356                ie = NULL;
 357
 358        wdev_lock(wdev);
 359
 360        /* no change */
 361        err = 0;
 362        if (wdev->wext.ie_len == ie_len &&
 363            memcmp(wdev->wext.ie, ie, ie_len) == 0)
 364                goto out;
 365
 366        if (ie_len) {
 367                ie = kmemdup(extra, ie_len, GFP_KERNEL);
 368                if (!ie) {
 369                        err = -ENOMEM;
 370                        goto out;
 371                }
 372        } else
 373                ie = NULL;
 374
 375        kfree(wdev->wext.ie);
 376        wdev->wext.ie = ie;
 377        wdev->wext.ie_len = ie_len;
 378
 379        if (wdev->sme_state != CFG80211_SME_IDLE) {
 380                err = __cfg80211_disconnect(rdev, dev,
 381                                            WLAN_REASON_DEAUTH_LEAVING, false);
 382                if (err)
 383                        goto out;
 384        }
 385
 386        /* userspace better not think we'll reconnect */
 387        err = 0;
 388 out:
 389        wdev_unlock(wdev);
 390        return err;
 391}
 392
 393int cfg80211_wext_siwmlme(struct net_device *dev,
 394                          struct iw_request_info *info,
 395                          struct iw_point *data, char *extra)
 396{
 397        struct wireless_dev *wdev = dev->ieee80211_ptr;
 398        struct iw_mlme *mlme = (struct iw_mlme *)extra;
 399        struct cfg80211_registered_device *rdev;
 400        int err;
 401
 402        if (!wdev)
 403                return -EOPNOTSUPP;
 404
 405        rdev = wiphy_to_dev(wdev->wiphy);
 406
 407        if (wdev->iftype != NL80211_IFTYPE_STATION)
 408                return -EINVAL;
 409
 410        if (mlme->addr.sa_family != ARPHRD_ETHER)
 411                return -EINVAL;
 412
 413        wdev_lock(wdev);
 414        switch (mlme->cmd) {
 415        case IW_MLME_DEAUTH:
 416        case IW_MLME_DISASSOC:
 417                err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
 418                                            true);
 419                break;
 420        default:
 421                err = -EOPNOTSUPP;
 422                break;
 423        }
 424        wdev_unlock(wdev);
 425
 426        return err;
 427}
 428