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