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