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