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, 2020-2023 Intel Corporation 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 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 ASSERT_RTNL(); 26 lockdep_assert_wiphy(wdev->wiphy); 27 28 if (!netif_running(wdev->netdev)) 29 return 0; 30 31 wdev->wext.connect.ie = wdev->wext.ie; 32 wdev->wext.connect.ie_len = wdev->wext.ie_len; 33 34 /* Use default background scan period */ 35 wdev->wext.connect.bg_scan_period = -1; 36 37 if (wdev->wext.keys) { 38 wdev->wext.keys->def = wdev->wext.default_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 && wdev->wext.keys->def != -1) { 47 ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); 48 if (!ck) 49 return -ENOMEM; 50 for (i = 0; i < 4; 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_sensitive(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_rdev(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(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 if (wdev->conn) { 91 bool event = true; 92 93 if (wdev->wext.connect.channel == chan) 94 return 0; 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 return err; 103 } 104 105 wdev->wext.connect.channel = chan; 106 return cfg80211_mgd_wext_connect(rdev, wdev); 107 } 108 109 int cfg80211_mgd_wext_giwfreq(struct net_device *dev, 110 struct iw_request_info *info, 111 struct iw_freq *freq, char *extra) 112 { 113 struct wireless_dev *wdev = dev->ieee80211_ptr; 114 struct ieee80211_channel *chan = NULL; 115 116 /* call only for station! */ 117 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 118 return -EINVAL; 119 120 if (wdev->valid_links) 121 return -EOPNOTSUPP; 122 123 if (wdev->links[0].client.current_bss) 124 chan = wdev->links[0].client.current_bss->pub.channel; 125 else if (wdev->wext.connect.channel) 126 chan = wdev->wext.connect.channel; 127 128 if (chan) { 129 freq->m = chan->center_freq; 130 freq->e = 6; 131 return 0; 132 } 133 134 /* no channel if not joining */ 135 return -EINVAL; 136 } 137 138 int cfg80211_mgd_wext_siwessid(struct net_device *dev, 139 struct iw_request_info *info, 140 struct iw_point *data, char *ssid) 141 { 142 struct wireless_dev *wdev = dev->ieee80211_ptr; 143 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 144 size_t len = data->length; 145 int err; 146 147 /* call only for station! */ 148 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 149 return -EINVAL; 150 151 if (!data->flags) 152 len = 0; 153 154 /* iwconfig uses nul termination in SSID.. */ 155 if (len > 0 && ssid[len - 1] == '\0') 156 len--; 157 158 if (wdev->conn) { 159 bool event = true; 160 161 if (wdev->wext.connect.ssid && len && 162 len == wdev->wext.connect.ssid_len && 163 memcmp(wdev->wext.connect.ssid, ssid, len) == 0) 164 return 0; 165 166 /* if SSID set now, we'll try to connect, avoid event */ 167 if (len) 168 event = false; 169 err = cfg80211_disconnect(rdev, dev, 170 WLAN_REASON_DEAUTH_LEAVING, event); 171 if (err) 172 return err; 173 } 174 175 wdev->wext.prev_bssid_valid = false; 176 wdev->wext.connect.ssid = wdev->wext.ssid; 177 memcpy(wdev->wext.ssid, ssid, len); 178 wdev->wext.connect.ssid_len = len; 179 180 wdev->wext.connect.crypto.control_port = false; 181 wdev->wext.connect.crypto.control_port_ethertype = 182 cpu_to_be16(ETH_P_PAE); 183 184 return cfg80211_mgd_wext_connect(rdev, wdev); 185 } 186 187 int cfg80211_mgd_wext_giwessid(struct net_device *dev, 188 struct iw_request_info *info, 189 struct iw_point *data, char *ssid) 190 { 191 struct wireless_dev *wdev = dev->ieee80211_ptr; 192 int ret = 0; 193 194 /* call only for station! */ 195 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 196 return -EINVAL; 197 198 if (wdev->valid_links) 199 return -EINVAL; 200 201 data->flags = 0; 202 203 if (wdev->links[0].client.current_bss) { 204 const struct element *ssid_elem; 205 206 rcu_read_lock(); 207 ssid_elem = ieee80211_bss_get_elem( 208 &wdev->links[0].client.current_bss->pub, 209 WLAN_EID_SSID); 210 if (ssid_elem) { 211 data->flags = 1; 212 data->length = ssid_elem->datalen; 213 if (data->length > IW_ESSID_MAX_SIZE) 214 ret = -EINVAL; 215 else 216 memcpy(ssid, ssid_elem->data, data->length); 217 } 218 rcu_read_unlock(); 219 } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { 220 data->flags = 1; 221 data->length = wdev->wext.connect.ssid_len; 222 memcpy(ssid, wdev->wext.connect.ssid, data->length); 223 } 224 225 return ret; 226 } 227 228 int cfg80211_mgd_wext_siwap(struct net_device *dev, 229 struct iw_request_info *info, 230 struct sockaddr *ap_addr, char *extra) 231 { 232 struct wireless_dev *wdev = dev->ieee80211_ptr; 233 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 234 u8 *bssid = ap_addr->sa_data; 235 int err; 236 237 /* call only for station! */ 238 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 239 return -EINVAL; 240 241 if (ap_addr->sa_family != ARPHRD_ETHER) 242 return -EINVAL; 243 244 /* automatic mode */ 245 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) 246 bssid = NULL; 247 248 if (wdev->conn) { 249 /* both automatic */ 250 if (!bssid && !wdev->wext.connect.bssid) 251 return 0; 252 253 /* fixed already - and no change */ 254 if (wdev->wext.connect.bssid && bssid && 255 ether_addr_equal(bssid, wdev->wext.connect.bssid)) 256 return 0; 257 258 err = cfg80211_disconnect(rdev, dev, 259 WLAN_REASON_DEAUTH_LEAVING, false); 260 if (err) 261 return err; 262 } 263 264 if (bssid) { 265 memcpy(wdev->wext.bssid, bssid, ETH_ALEN); 266 wdev->wext.connect.bssid = wdev->wext.bssid; 267 } else 268 wdev->wext.connect.bssid = NULL; 269 270 return cfg80211_mgd_wext_connect(rdev, wdev); 271 } 272 273 int cfg80211_mgd_wext_giwap(struct net_device *dev, 274 struct iw_request_info *info, 275 struct sockaddr *ap_addr, char *extra) 276 { 277 struct wireless_dev *wdev = dev->ieee80211_ptr; 278 279 /* call only for station! */ 280 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 281 return -EINVAL; 282 283 ap_addr->sa_family = ARPHRD_ETHER; 284 285 if (wdev->valid_links) 286 return -EOPNOTSUPP; 287 288 if (wdev->links[0].client.current_bss) 289 memcpy(ap_addr->sa_data, 290 wdev->links[0].client.current_bss->pub.bssid, 291 ETH_ALEN); 292 else 293 eth_zero_addr(ap_addr->sa_data); 294 295 return 0; 296 } 297 298 int cfg80211_wext_siwgenie(struct net_device *dev, 299 struct iw_request_info *info, 300 union iwreq_data *wrqu, char *extra) 301 { 302 struct iw_point *data = &wrqu->data; 303 struct wireless_dev *wdev = dev->ieee80211_ptr; 304 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 305 u8 *ie = extra; 306 int ie_len = data->length, err; 307 308 if (wdev->iftype != NL80211_IFTYPE_STATION) 309 return -EOPNOTSUPP; 310 311 if (!ie_len) 312 ie = NULL; 313 314 wiphy_lock(wdev->wiphy); 315 316 /* no change */ 317 err = 0; 318 if (wdev->wext.ie_len == ie_len && 319 memcmp(wdev->wext.ie, ie, ie_len) == 0) 320 goto out; 321 322 if (ie_len) { 323 ie = kmemdup(extra, ie_len, GFP_KERNEL); 324 if (!ie) { 325 err = -ENOMEM; 326 goto out; 327 } 328 } else 329 ie = NULL; 330 331 kfree(wdev->wext.ie); 332 wdev->wext.ie = ie; 333 wdev->wext.ie_len = ie_len; 334 335 if (wdev->conn) { 336 err = cfg80211_disconnect(rdev, dev, 337 WLAN_REASON_DEAUTH_LEAVING, false); 338 if (err) 339 goto out; 340 } 341 342 /* userspace better not think we'll reconnect */ 343 err = 0; 344 out: 345 wiphy_unlock(wdev->wiphy); 346 return err; 347 } 348 349 int cfg80211_wext_siwmlme(struct net_device *dev, 350 struct iw_request_info *info, 351 union iwreq_data *wrqu, char *extra) 352 { 353 struct wireless_dev *wdev = dev->ieee80211_ptr; 354 struct iw_mlme *mlme = (struct iw_mlme *)extra; 355 struct cfg80211_registered_device *rdev; 356 int err; 357 358 if (!wdev) 359 return -EOPNOTSUPP; 360 361 rdev = wiphy_to_rdev(wdev->wiphy); 362 363 if (wdev->iftype != NL80211_IFTYPE_STATION) 364 return -EINVAL; 365 366 if (mlme->addr.sa_family != ARPHRD_ETHER) 367 return -EINVAL; 368 369 wiphy_lock(&rdev->wiphy); 370 switch (mlme->cmd) { 371 case IW_MLME_DEAUTH: 372 case IW_MLME_DISASSOC: 373 err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true); 374 break; 375 default: 376 err = -EOPNOTSUPP; 377 break; 378 } 379 wiphy_unlock(&rdev->wiphy); 380 381 return err; 382 } 383
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.