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_RTNL(); 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 wdev_lock(wdev); 91 92 if (wdev->conn) { 93 bool event = true; 94 95 if (wdev->wext.connect.channel == chan) { 96 err = 0; 97 goto out; 98 } 99 100 /* if SSID set, we'll try right again, avoid event */ 101 if (wdev->wext.connect.ssid_len) 102 event = false; 103 err = cfg80211_disconnect(rdev, dev, 104 WLAN_REASON_DEAUTH_LEAVING, event); 105 if (err) 106 goto out; 107 } 108 109 110 wdev->wext.connect.channel = chan; 111 112 /* 113 * SSID is not set, we just want to switch monitor channel, 114 * this is really just backward compatibility, if the SSID 115 * is set then we use the channel to select the BSS to use 116 * to connect to instead. If we were connected on another 117 * channel we disconnected above and reconnect below. 118 */ 119 if (chan && !wdev->wext.connect.ssid_len) { 120 struct cfg80211_chan_def chandef = { 121 .width = NL80211_CHAN_WIDTH_20_NOHT, 122 .center_freq1 = freq, 123 }; 124 125 chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); 126 if (chandef.chan) 127 err = cfg80211_set_monitor_channel(rdev, &chandef); 128 else 129 err = -EINVAL; 130 goto out; 131 } 132 133 err = cfg80211_mgd_wext_connect(rdev, wdev); 134 out: 135 wdev_unlock(wdev); 136 return err; 137 } 138 139 int cfg80211_mgd_wext_giwfreq(struct net_device *dev, 140 struct iw_request_info *info, 141 struct iw_freq *freq, char *extra) 142 { 143 struct wireless_dev *wdev = dev->ieee80211_ptr; 144 struct ieee80211_channel *chan = NULL; 145 146 /* call only for station! */ 147 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 148 return -EINVAL; 149 150 wdev_lock(wdev); 151 if (wdev->current_bss) 152 chan = wdev->current_bss->pub.channel; 153 else if (wdev->wext.connect.channel) 154 chan = wdev->wext.connect.channel; 155 wdev_unlock(wdev); 156 157 if (chan) { 158 freq->m = chan->center_freq; 159 freq->e = 6; 160 return 0; 161 } 162 163 /* no channel if not joining */ 164 return -EINVAL; 165 } 166 167 int cfg80211_mgd_wext_siwessid(struct net_device *dev, 168 struct iw_request_info *info, 169 struct iw_point *data, char *ssid) 170 { 171 struct wireless_dev *wdev = dev->ieee80211_ptr; 172 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 173 size_t len = data->length; 174 int err; 175 176 /* call only for station! */ 177 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 178 return -EINVAL; 179 180 if (!data->flags) 181 len = 0; 182 183 /* iwconfig uses nul termination in SSID.. */ 184 if (len > 0 && ssid[len - 1] == '\0') 185 len--; 186 187 wdev_lock(wdev); 188 189 err = 0; 190 191 if (wdev->conn) { 192 bool event = true; 193 194 if (wdev->wext.connect.ssid && len && 195 len == wdev->wext.connect.ssid_len && 196 memcmp(wdev->wext.connect.ssid, ssid, len) == 0) 197 goto out; 198 199 /* if SSID set now, we'll try to connect, avoid event */ 200 if (len) 201 event = false; 202 err = cfg80211_disconnect(rdev, dev, 203 WLAN_REASON_DEAUTH_LEAVING, event); 204 if (err) 205 goto out; 206 } 207 208 wdev->wext.prev_bssid_valid = false; 209 wdev->wext.connect.ssid = wdev->wext.ssid; 210 memcpy(wdev->wext.ssid, ssid, len); 211 wdev->wext.connect.ssid_len = len; 212 213 wdev->wext.connect.crypto.control_port = false; 214 wdev->wext.connect.crypto.control_port_ethertype = 215 cpu_to_be16(ETH_P_PAE); 216 217 err = cfg80211_mgd_wext_connect(rdev, wdev); 218 out: 219 wdev_unlock(wdev); 220 return err; 221 } 222 223 int cfg80211_mgd_wext_giwessid(struct net_device *dev, 224 struct iw_request_info *info, 225 struct iw_point *data, char *ssid) 226 { 227 struct wireless_dev *wdev = dev->ieee80211_ptr; 228 229 /* call only for station! */ 230 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 231 return -EINVAL; 232 233 data->flags = 0; 234 235 wdev_lock(wdev); 236 if (wdev->current_bss) { 237 const u8 *ie; 238 239 rcu_read_lock(); 240 ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, 241 WLAN_EID_SSID); 242 if (ie) { 243 data->flags = 1; 244 data->length = ie[1]; 245 memcpy(ssid, ie + 2, data->length); 246 } 247 rcu_read_unlock(); 248 } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { 249 data->flags = 1; 250 data->length = wdev->wext.connect.ssid_len; 251 memcpy(ssid, wdev->wext.connect.ssid, data->length); 252 } 253 wdev_unlock(wdev); 254 255 return 0; 256 } 257 258 int cfg80211_mgd_wext_siwap(struct net_device *dev, 259 struct iw_request_info *info, 260 struct sockaddr *ap_addr, char *extra) 261 { 262 struct wireless_dev *wdev = dev->ieee80211_ptr; 263 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 264 u8 *bssid = ap_addr->sa_data; 265 int err; 266 267 /* call only for station! */ 268 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 269 return -EINVAL; 270 271 if (ap_addr->sa_family != ARPHRD_ETHER) 272 return -EINVAL; 273 274 /* automatic mode */ 275 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) 276 bssid = NULL; 277 278 wdev_lock(wdev); 279 280 if (wdev->conn) { 281 err = 0; 282 /* both automatic */ 283 if (!bssid && !wdev->wext.connect.bssid) 284 goto out; 285 286 /* fixed already - and no change */ 287 if (wdev->wext.connect.bssid && bssid && 288 ether_addr_equal(bssid, wdev->wext.connect.bssid)) 289 goto out; 290 291 err = cfg80211_disconnect(rdev, dev, 292 WLAN_REASON_DEAUTH_LEAVING, false); 293 if (err) 294 goto out; 295 } 296 297 if (bssid) { 298 memcpy(wdev->wext.bssid, bssid, ETH_ALEN); 299 wdev->wext.connect.bssid = wdev->wext.bssid; 300 } else 301 wdev->wext.connect.bssid = NULL; 302 303 err = cfg80211_mgd_wext_connect(rdev, wdev); 304 out: 305 wdev_unlock(wdev); 306 return err; 307 } 308 309 int cfg80211_mgd_wext_giwap(struct net_device *dev, 310 struct iw_request_info *info, 311 struct sockaddr *ap_addr, char *extra) 312 { 313 struct wireless_dev *wdev = dev->ieee80211_ptr; 314 315 /* call only for station! */ 316 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 317 return -EINVAL; 318 319 ap_addr->sa_family = ARPHRD_ETHER; 320 321 wdev_lock(wdev); 322 if (wdev->current_bss) 323 memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); 324 else 325 memset(ap_addr->sa_data, 0, ETH_ALEN); 326 wdev_unlock(wdev); 327 328 return 0; 329 } 330 331 int cfg80211_wext_siwgenie(struct net_device *dev, 332 struct iw_request_info *info, 333 struct iw_point *data, char *extra) 334 { 335 struct wireless_dev *wdev = dev->ieee80211_ptr; 336 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 337 u8 *ie = extra; 338 int ie_len = data->length, err; 339 340 if (wdev->iftype != NL80211_IFTYPE_STATION) 341 return -EOPNOTSUPP; 342 343 if (!ie_len) 344 ie = NULL; 345 346 wdev_lock(wdev); 347 348 /* no change */ 349 err = 0; 350 if (wdev->wext.ie_len == ie_len && 351 memcmp(wdev->wext.ie, ie, ie_len) == 0) 352 goto out; 353 354 if (ie_len) { 355 ie = kmemdup(extra, ie_len, GFP_KERNEL); 356 if (!ie) { 357 err = -ENOMEM; 358 goto out; 359 } 360 } else 361 ie = NULL; 362 363 kfree(wdev->wext.ie); 364 wdev->wext.ie = ie; 365 wdev->wext.ie_len = ie_len; 366 367 if (wdev->conn) { 368 err = cfg80211_disconnect(rdev, dev, 369 WLAN_REASON_DEAUTH_LEAVING, false); 370 if (err) 371 goto out; 372 } 373 374 /* userspace better not think we'll reconnect */ 375 err = 0; 376 out: 377 wdev_unlock(wdev); 378 return err; 379 } 380 381 int cfg80211_wext_siwmlme(struct net_device *dev, 382 struct iw_request_info *info, 383 struct iw_point *data, char *extra) 384 { 385 struct wireless_dev *wdev = dev->ieee80211_ptr; 386 struct iw_mlme *mlme = (struct iw_mlme *)extra; 387 struct cfg80211_registered_device *rdev; 388 int err; 389 390 if (!wdev) 391 return -EOPNOTSUPP; 392 393 rdev = wiphy_to_dev(wdev->wiphy); 394 395 if (wdev->iftype != NL80211_IFTYPE_STATION) 396 return -EINVAL; 397 398 if (mlme->addr.sa_family != ARPHRD_ETHER) 399 return -EINVAL; 400 401 wdev_lock(wdev); 402 switch (mlme->cmd) { 403 case IW_MLME_DEAUTH: 404 case IW_MLME_DISASSOC: 405 err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true); 406 break; 407 default: 408 err = -EOPNOTSUPP; 409 break; 410 } 411 wdev_unlock(wdev); 412 413 return err; 414 } 415