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