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