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