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