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 17 int 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 65 int 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 143 int 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 171 int 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 231 int 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 266 int 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 321 int 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 343 int 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 393 int 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