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                err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
 123                goto out;
 124        }
 125
 126        err = cfg80211_mgd_wext_connect(rdev, wdev);
 127 out:
 128        wdev_unlock(wdev);
 129        mutex_unlock(&rdev->devlist_mtx);
 130        cfg80211_unlock_rdev(rdev);
 131        return err;
 132}
 133
 134int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
 135                              struct iw_request_info *info,
 136                              struct iw_freq *freq, char *extra)
 137{
 138        struct wireless_dev *wdev = dev->ieee80211_ptr;
 139        struct ieee80211_channel *chan = NULL;
 140
 141        /* call only for station! */
 142        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 143                return -EINVAL;
 144
 145        wdev_lock(wdev);
 146        if (wdev->current_bss)
 147                chan = wdev->current_bss->pub.channel;
 148        else if (wdev->wext.connect.channel)
 149                chan = wdev->wext.connect.channel;
 150        wdev_unlock(wdev);
 151
 152        if (chan) {
 153                freq->m = chan->center_freq;
 154                freq->e = 6;
 155                return 0;
 156        }
 157
 158        /* no channel if not joining */
 159        return -EINVAL;
 160}
 161
 162int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 163                               struct iw_request_info *info,
 164                               struct iw_point *data, char *ssid)
 165{
 166        struct wireless_dev *wdev = dev->ieee80211_ptr;
 167        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 168        size_t len = data->length;
 169        int err;
 170
 171        /* call only for station! */
 172        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 173                return -EINVAL;
 174
 175        if (!data->flags)
 176                len = 0;
 177
 178        /* iwconfig uses nul termination in SSID.. */
 179        if (len > 0 && ssid[len - 1] == '\0')
 180                len--;
 181
 182        cfg80211_lock_rdev(rdev);
 183        mutex_lock(&rdev->devlist_mtx);
 184        wdev_lock(wdev);
 185
 186        err = 0;
 187
 188        if (wdev->sme_state != CFG80211_SME_IDLE) {
 189                bool event = true;
 190
 191                if (wdev->wext.connect.ssid && len &&
 192                    len == wdev->wext.connect.ssid_len &&
 193                    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
 194                        goto out;
 195
 196                /* if SSID set now, we'll try to connect, avoid event */
 197                if (len)
 198                        event = false;
 199                err = __cfg80211_disconnect(rdev, dev,
 200                                            WLAN_REASON_DEAUTH_LEAVING, event);
 201                if (err)
 202                        goto out;
 203        }
 204
 205        wdev->wext.prev_bssid_valid = false;
 206        wdev->wext.connect.ssid = wdev->wext.ssid;
 207        memcpy(wdev->wext.ssid, ssid, len);
 208        wdev->wext.connect.ssid_len = len;
 209
 210        wdev->wext.connect.crypto.control_port = false;
 211        wdev->wext.connect.crypto.control_port_ethertype =
 212                                        cpu_to_be16(ETH_P_PAE);
 213
 214        err = cfg80211_mgd_wext_connect(rdev, wdev);
 215 out:
 216        wdev_unlock(wdev);
 217        mutex_unlock(&rdev->devlist_mtx);
 218        cfg80211_unlock_rdev(rdev);
 219        return err;
 220}
 221
 222int cfg80211_mgd_wext_giwessid(struct net_device *dev,
 223                               struct iw_request_info *info,
 224                               struct iw_point *data, char *ssid)
 225{
 226        struct wireless_dev *wdev = dev->ieee80211_ptr;
 227
 228        /* call only for station! */
 229        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 230                return -EINVAL;
 231
 232        data->flags = 0;
 233
 234        wdev_lock(wdev);
 235        if (wdev->current_bss) {
 236                const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
 237                                                    WLAN_EID_SSID);
 238                if (ie) {
 239                        data->flags = 1;
 240                        data->length = ie[1];
 241                        memcpy(ssid, ie + 2, data->length);
 242                }
 243        } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
 244                data->flags = 1;
 245                data->length = wdev->wext.connect.ssid_len;
 246                memcpy(ssid, wdev->wext.connect.ssid, data->length);
 247        }
 248        wdev_unlock(wdev);
 249
 250        return 0;
 251}
 252
 253int cfg80211_mgd_wext_siwap(struct net_device *dev,
 254                            struct iw_request_info *info,
 255                            struct sockaddr *ap_addr, char *extra)
 256{
 257        struct wireless_dev *wdev = dev->ieee80211_ptr;
 258        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 259        u8 *bssid = ap_addr->sa_data;
 260        int err;
 261
 262        /* call only for station! */
 263        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 264                return -EINVAL;
 265
 266        if (ap_addr->sa_family != ARPHRD_ETHER)
 267                return -EINVAL;
 268
 269        /* automatic mode */
 270        if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
 271                bssid = NULL;
 272
 273        cfg80211_lock_rdev(rdev);
 274        mutex_lock(&rdev->devlist_mtx);
 275        wdev_lock(wdev);
 276
 277        if (wdev->sme_state != CFG80211_SME_IDLE) {
 278                err = 0;
 279                /* both automatic */
 280                if (!bssid && !wdev->wext.connect.bssid)
 281                        goto out;
 282
 283                /* fixed already - and no change */
 284                if (wdev->wext.connect.bssid && bssid &&
 285                    ether_addr_equal(bssid, wdev->wext.connect.bssid))
 286                        goto out;
 287
 288                err = __cfg80211_disconnect(rdev, dev,
 289                                            WLAN_REASON_DEAUTH_LEAVING, false);
 290                if (err)
 291                        goto out;
 292        }
 293
 294        if (bssid) {
 295                memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
 296                wdev->wext.connect.bssid = wdev->wext.bssid;
 297        } else
 298                wdev->wext.connect.bssid = NULL;
 299
 300        err = cfg80211_mgd_wext_connect(rdev, wdev);
 301 out:
 302        wdev_unlock(wdev);
 303        mutex_unlock(&rdev->devlist_mtx);
 304        cfg80211_unlock_rdev(rdev);
 305        return err;
 306}
 307
 308int cfg80211_mgd_wext_giwap(struct net_device *dev,
 309                            struct iw_request_info *info,
 310                            struct sockaddr *ap_addr, char *extra)
 311{
 312        struct wireless_dev *wdev = dev->ieee80211_ptr;
 313
 314        /* call only for station! */
 315        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
 316                return -EINVAL;
 317
 318        ap_addr->sa_family = ARPHRD_ETHER;
 319
 320        wdev_lock(wdev);
 321        if (wdev->current_bss)
 322                memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
 323        else
 324                memset(ap_addr->sa_data, 0, ETH_ALEN);
 325        wdev_unlock(wdev);
 326
 327        return 0;
 328}
 329
 330int cfg80211_wext_siwgenie(struct net_device *dev,
 331                           struct iw_request_info *info,
 332                           struct iw_point *data, char *extra)
 333{
 334        struct wireless_dev *wdev = dev->ieee80211_ptr;
 335        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 336        u8 *ie = extra;
 337        int ie_len = data->length, err;
 338
 339        if (wdev->iftype != NL80211_IFTYPE_STATION)
 340                return -EOPNOTSUPP;
 341
 342        if (!ie_len)
 343                ie = NULL;
 344
 345        wdev_lock(wdev);
 346
 347        /* no change */
 348        err = 0;
 349        if (wdev->wext.ie_len == ie_len &&
 350            memcmp(wdev->wext.ie, ie, ie_len) == 0)
 351                goto out;
 352
 353        if (ie_len) {
 354                ie = kmemdup(extra, ie_len, GFP_KERNEL);
 355                if (!ie) {
 356                        err = -ENOMEM;
 357                        goto out;
 358                }
 359        } else
 360                ie = NULL;
 361
 362        kfree(wdev->wext.ie);
 363        wdev->wext.ie = ie;
 364        wdev->wext.ie_len = ie_len;
 365
 366        if (wdev->sme_state != CFG80211_SME_IDLE) {
 367                err = __cfg80211_disconnect(rdev, dev,
 368                                            WLAN_REASON_DEAUTH_LEAVING, false);
 369                if (err)
 370                        goto out;
 371        }
 372
 373        /* userspace better not think we'll reconnect */
 374        err = 0;
 375 out:
 376        wdev_unlock(wdev);
 377        return err;
 378}
 379
 380int cfg80211_wext_siwmlme(struct net_device *dev,
 381                          struct iw_request_info *info,
 382                          struct iw_point *data, char *extra)
 383{
 384        struct wireless_dev *wdev = dev->ieee80211_ptr;
 385        struct iw_mlme *mlme = (struct iw_mlme *)extra;
 386        struct cfg80211_registered_device *rdev;
 387        int err;
 388
 389        if (!wdev)
 390                return -EOPNOTSUPP;
 391
 392        rdev = wiphy_to_dev(wdev->wiphy);
 393
 394        if (wdev->iftype != NL80211_IFTYPE_STATION)
 395                return -EINVAL;
 396
 397        if (mlme->addr.sa_family != ARPHRD_ETHER)
 398                return -EINVAL;
 399
 400        wdev_lock(wdev);
 401        switch (mlme->cmd) {
 402        case IW_MLME_DEAUTH:
 403        case IW_MLME_DISASSOC:
 404                err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
 405                                            true);
 406                break;
 407        default:
 408                err = -EOPNOTSUPP;
 409                break;
 410        }
 411        wdev_unlock(wdev);
 412
 413        return err;
 414}
 415