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 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 134 int 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 162 int 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 222 int 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 253 int 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 308 int 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 330 int 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 380 int 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