1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Some IBSS support code for cfg80211. |
4 | * |
5 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright (C) 2020-2023 Intel Corporation |
7 | */ |
8 | |
9 | #include <linux/etherdevice.h> |
10 | #include <linux/if_arp.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/export.h> |
13 | #include <net/cfg80211.h> |
14 | #include "wext-compat.h" |
15 | #include "nl80211.h" |
16 | #include "rdev-ops.h" |
17 | |
18 | |
19 | void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, |
20 | struct ieee80211_channel *channel) |
21 | { |
22 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
23 | struct cfg80211_bss *bss; |
24 | #ifdef CONFIG_CFG80211_WEXT |
25 | union iwreq_data wrqu; |
26 | #endif |
27 | |
28 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
29 | return; |
30 | |
31 | if (!wdev->u.ibss.ssid_len) |
32 | return; |
33 | |
34 | bss = cfg80211_get_bss(wiphy: wdev->wiphy, channel, bssid, NULL, ssid_len: 0, |
35 | bss_type: IEEE80211_BSS_TYPE_IBSS, privacy: IEEE80211_PRIVACY_ANY); |
36 | |
37 | if (WARN_ON(!bss)) |
38 | return; |
39 | |
40 | if (wdev->u.ibss.current_bss) { |
41 | cfg80211_unhold_bss(bss: wdev->u.ibss.current_bss); |
42 | cfg80211_put_bss(wiphy: wdev->wiphy, bss: &wdev->u.ibss.current_bss->pub); |
43 | } |
44 | |
45 | cfg80211_hold_bss(bss: bss_from_pub(pub: bss)); |
46 | wdev->u.ibss.current_bss = bss_from_pub(pub: bss); |
47 | |
48 | cfg80211_upload_connect_keys(wdev); |
49 | |
50 | nl80211_send_ibss_bssid(rdev: wiphy_to_rdev(wiphy: wdev->wiphy), netdev: dev, bssid, |
51 | GFP_KERNEL); |
52 | #ifdef CONFIG_CFG80211_WEXT |
53 | memset(&wrqu, 0, sizeof(wrqu)); |
54 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); |
55 | wireless_send_event(dev, SIOCGIWAP, wrqu: &wrqu, NULL); |
56 | #endif |
57 | } |
58 | |
59 | void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, |
60 | struct ieee80211_channel *channel, gfp_t gfp) |
61 | { |
62 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
63 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
64 | struct cfg80211_event *ev; |
65 | unsigned long flags; |
66 | |
67 | trace_cfg80211_ibss_joined(netdev: dev, bssid, channel); |
68 | |
69 | if (WARN_ON(!channel)) |
70 | return; |
71 | |
72 | ev = kzalloc(size: sizeof(*ev), flags: gfp); |
73 | if (!ev) |
74 | return; |
75 | |
76 | ev->type = EVENT_IBSS_JOINED; |
77 | memcpy(ev->ij.bssid, bssid, ETH_ALEN); |
78 | ev->ij.channel = channel; |
79 | |
80 | spin_lock_irqsave(&wdev->event_lock, flags); |
81 | list_add_tail(new: &ev->list, head: &wdev->event_list); |
82 | spin_unlock_irqrestore(lock: &wdev->event_lock, flags); |
83 | queue_work(wq: cfg80211_wq, work: &rdev->event_work); |
84 | } |
85 | EXPORT_SYMBOL(cfg80211_ibss_joined); |
86 | |
87 | int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, |
88 | struct net_device *dev, |
89 | struct cfg80211_ibss_params *params, |
90 | struct cfg80211_cached_keys *connkeys) |
91 | { |
92 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
93 | int err; |
94 | |
95 | lockdep_assert_held(&rdev->wiphy.mtx); |
96 | |
97 | if (wdev->u.ibss.ssid_len) |
98 | return -EALREADY; |
99 | |
100 | if (!params->basic_rates) { |
101 | /* |
102 | * If no rates were explicitly configured, |
103 | * use the mandatory rate set for 11b or |
104 | * 11a for maximum compatibility. |
105 | */ |
106 | struct ieee80211_supported_band *sband; |
107 | enum nl80211_band band; |
108 | u32 flag; |
109 | int j; |
110 | |
111 | band = params->chandef.chan->band; |
112 | if (band == NL80211_BAND_5GHZ || |
113 | band == NL80211_BAND_6GHZ) |
114 | flag = IEEE80211_RATE_MANDATORY_A; |
115 | else |
116 | flag = IEEE80211_RATE_MANDATORY_B; |
117 | |
118 | sband = rdev->wiphy.bands[band]; |
119 | for (j = 0; j < sband->n_bitrates; j++) { |
120 | if (sband->bitrates[j].flags & flag) |
121 | params->basic_rates |= BIT(j); |
122 | } |
123 | } |
124 | |
125 | if (WARN_ON(connkeys && connkeys->def < 0)) |
126 | return -EINVAL; |
127 | |
128 | if (WARN_ON(wdev->connect_keys)) |
129 | kfree_sensitive(objp: wdev->connect_keys); |
130 | wdev->connect_keys = connkeys; |
131 | |
132 | wdev->u.ibss.chandef = params->chandef; |
133 | if (connkeys) { |
134 | params->wep_keys = connkeys->params; |
135 | params->wep_tx_key = connkeys->def; |
136 | } |
137 | |
138 | #ifdef CONFIG_CFG80211_WEXT |
139 | wdev->wext.ibss.chandef = params->chandef; |
140 | #endif |
141 | err = rdev_join_ibss(rdev, dev, params); |
142 | if (err) { |
143 | wdev->connect_keys = NULL; |
144 | return err; |
145 | } |
146 | |
147 | memcpy(wdev->u.ibss.ssid, params->ssid, params->ssid_len); |
148 | wdev->u.ibss.ssid_len = params->ssid_len; |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | void cfg80211_clear_ibss(struct net_device *dev, bool nowext) |
154 | { |
155 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
156 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
157 | int i; |
158 | |
159 | lockdep_assert_wiphy(wdev->wiphy); |
160 | |
161 | kfree_sensitive(objp: wdev->connect_keys); |
162 | wdev->connect_keys = NULL; |
163 | |
164 | rdev_set_qos_map(rdev, dev, NULL); |
165 | |
166 | /* |
167 | * Delete all the keys ... pairwise keys can't really |
168 | * exist any more anyway, but default keys might. |
169 | */ |
170 | if (rdev->ops->del_key) |
171 | for (i = 0; i < 6; i++) |
172 | rdev_del_key(rdev, netdev: dev, link_id: -1, key_index: i, pairwise: false, NULL); |
173 | |
174 | if (wdev->u.ibss.current_bss) { |
175 | cfg80211_unhold_bss(bss: wdev->u.ibss.current_bss); |
176 | cfg80211_put_bss(wiphy: wdev->wiphy, bss: &wdev->u.ibss.current_bss->pub); |
177 | } |
178 | |
179 | wdev->u.ibss.current_bss = NULL; |
180 | wdev->u.ibss.ssid_len = 0; |
181 | memset(&wdev->u.ibss.chandef, 0, sizeof(wdev->u.ibss.chandef)); |
182 | #ifdef CONFIG_CFG80211_WEXT |
183 | if (!nowext) |
184 | wdev->wext.ibss.ssid_len = 0; |
185 | #endif |
186 | cfg80211_sched_dfs_chan_update(rdev); |
187 | } |
188 | |
189 | int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, |
190 | struct net_device *dev, bool nowext) |
191 | { |
192 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
193 | int err; |
194 | |
195 | lockdep_assert_wiphy(wdev->wiphy); |
196 | |
197 | if (!wdev->u.ibss.ssid_len) |
198 | return -ENOLINK; |
199 | |
200 | err = rdev_leave_ibss(rdev, dev); |
201 | |
202 | if (err) |
203 | return err; |
204 | |
205 | wdev->conn_owner_nlportid = 0; |
206 | cfg80211_clear_ibss(dev, nowext); |
207 | |
208 | return 0; |
209 | } |
210 | |
211 | #ifdef CONFIG_CFG80211_WEXT |
212 | int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, |
213 | struct wireless_dev *wdev) |
214 | { |
215 | struct cfg80211_cached_keys *ck = NULL; |
216 | enum nl80211_band band; |
217 | int i, err; |
218 | |
219 | lockdep_assert_wiphy(wdev->wiphy); |
220 | |
221 | if (!wdev->wext.ibss.beacon_interval) |
222 | wdev->wext.ibss.beacon_interval = 100; |
223 | |
224 | /* try to find an IBSS channel if none requested ... */ |
225 | if (!wdev->wext.ibss.chandef.chan) { |
226 | struct ieee80211_channel *new_chan = NULL; |
227 | |
228 | for (band = 0; band < NUM_NL80211_BANDS; band++) { |
229 | struct ieee80211_supported_band *sband; |
230 | struct ieee80211_channel *chan; |
231 | |
232 | sband = rdev->wiphy.bands[band]; |
233 | if (!sband) |
234 | continue; |
235 | |
236 | for (i = 0; i < sband->n_channels; i++) { |
237 | chan = &sband->channels[i]; |
238 | if (chan->flags & IEEE80211_CHAN_NO_IR) |
239 | continue; |
240 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
241 | continue; |
242 | new_chan = chan; |
243 | break; |
244 | } |
245 | |
246 | if (new_chan) |
247 | break; |
248 | } |
249 | |
250 | if (!new_chan) |
251 | return -EINVAL; |
252 | |
253 | cfg80211_chandef_create(chandef: &wdev->wext.ibss.chandef, channel: new_chan, |
254 | chantype: NL80211_CHAN_NO_HT); |
255 | } |
256 | |
257 | /* don't join -- SSID is not there */ |
258 | if (!wdev->wext.ibss.ssid_len) |
259 | return 0; |
260 | |
261 | if (!netif_running(dev: wdev->netdev)) |
262 | return 0; |
263 | |
264 | if (wdev->wext.keys) |
265 | wdev->wext.keys->def = wdev->wext.default_key; |
266 | |
267 | wdev->wext.ibss.privacy = wdev->wext.default_key != -1; |
268 | |
269 | if (wdev->wext.keys && wdev->wext.keys->def != -1) { |
270 | ck = kmemdup(p: wdev->wext.keys, size: sizeof(*ck), GFP_KERNEL); |
271 | if (!ck) |
272 | return -ENOMEM; |
273 | for (i = 0; i < 4; i++) |
274 | ck->params[i].key = ck->data[i]; |
275 | } |
276 | err = __cfg80211_join_ibss(rdev, dev: wdev->netdev, |
277 | params: &wdev->wext.ibss, connkeys: ck); |
278 | if (err) |
279 | kfree(objp: ck); |
280 | |
281 | return err; |
282 | } |
283 | |
284 | int cfg80211_ibss_wext_siwfreq(struct net_device *dev, |
285 | struct iw_request_info *info, |
286 | struct iw_freq *wextfreq, char *) |
287 | { |
288 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
289 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
290 | struct ieee80211_channel *chan = NULL; |
291 | int err, freq; |
292 | |
293 | /* call only for ibss! */ |
294 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
295 | return -EINVAL; |
296 | |
297 | if (!rdev->ops->join_ibss) |
298 | return -EOPNOTSUPP; |
299 | |
300 | freq = cfg80211_wext_freq(freq: wextfreq); |
301 | if (freq < 0) |
302 | return freq; |
303 | |
304 | if (freq) { |
305 | chan = ieee80211_get_channel(wiphy: wdev->wiphy, freq); |
306 | if (!chan) |
307 | return -EINVAL; |
308 | if (chan->flags & IEEE80211_CHAN_NO_IR || |
309 | chan->flags & IEEE80211_CHAN_DISABLED) |
310 | return -EINVAL; |
311 | } |
312 | |
313 | if (wdev->wext.ibss.chandef.chan == chan) |
314 | return 0; |
315 | |
316 | err = 0; |
317 | if (wdev->u.ibss.ssid_len) |
318 | err = cfg80211_leave_ibss(rdev, dev, nowext: true); |
319 | |
320 | if (err) |
321 | return err; |
322 | |
323 | if (chan) { |
324 | cfg80211_chandef_create(chandef: &wdev->wext.ibss.chandef, channel: chan, |
325 | chantype: NL80211_CHAN_NO_HT); |
326 | wdev->wext.ibss.channel_fixed = true; |
327 | } else { |
328 | /* cfg80211_ibss_wext_join will pick one if needed */ |
329 | wdev->wext.ibss.channel_fixed = false; |
330 | } |
331 | |
332 | return cfg80211_ibss_wext_join(rdev, wdev); |
333 | } |
334 | |
335 | int cfg80211_ibss_wext_giwfreq(struct net_device *dev, |
336 | struct iw_request_info *info, |
337 | struct iw_freq *freq, char *) |
338 | { |
339 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
340 | struct ieee80211_channel *chan = NULL; |
341 | |
342 | /* call only for ibss! */ |
343 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
344 | return -EINVAL; |
345 | |
346 | if (wdev->u.ibss.current_bss) |
347 | chan = wdev->u.ibss.current_bss->pub.channel; |
348 | else if (wdev->wext.ibss.chandef.chan) |
349 | chan = wdev->wext.ibss.chandef.chan; |
350 | |
351 | if (chan) { |
352 | freq->m = chan->center_freq; |
353 | freq->e = 6; |
354 | return 0; |
355 | } |
356 | |
357 | /* no channel if not joining */ |
358 | return -EINVAL; |
359 | } |
360 | |
361 | int cfg80211_ibss_wext_siwessid(struct net_device *dev, |
362 | struct iw_request_info *info, |
363 | struct iw_point *data, char *ssid) |
364 | { |
365 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
366 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
367 | size_t len = data->length; |
368 | int err; |
369 | |
370 | /* call only for ibss! */ |
371 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
372 | return -EINVAL; |
373 | |
374 | if (!rdev->ops->join_ibss) |
375 | return -EOPNOTSUPP; |
376 | |
377 | err = 0; |
378 | if (wdev->u.ibss.ssid_len) |
379 | err = cfg80211_leave_ibss(rdev, dev, nowext: true); |
380 | |
381 | if (err) |
382 | return err; |
383 | |
384 | /* iwconfig uses nul termination in SSID.. */ |
385 | if (len > 0 && ssid[len - 1] == '\0') |
386 | len--; |
387 | |
388 | memcpy(wdev->u.ibss.ssid, ssid, len); |
389 | wdev->wext.ibss.ssid = wdev->u.ibss.ssid; |
390 | wdev->wext.ibss.ssid_len = len; |
391 | |
392 | return cfg80211_ibss_wext_join(rdev, wdev); |
393 | } |
394 | |
395 | int cfg80211_ibss_wext_giwessid(struct net_device *dev, |
396 | struct iw_request_info *info, |
397 | struct iw_point *data, char *ssid) |
398 | { |
399 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
400 | |
401 | /* call only for ibss! */ |
402 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
403 | return -EINVAL; |
404 | |
405 | data->flags = 0; |
406 | |
407 | if (wdev->u.ibss.ssid_len) { |
408 | data->flags = 1; |
409 | data->length = wdev->u.ibss.ssid_len; |
410 | memcpy(ssid, wdev->u.ibss.ssid, data->length); |
411 | } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { |
412 | data->flags = 1; |
413 | data->length = wdev->wext.ibss.ssid_len; |
414 | memcpy(ssid, wdev->wext.ibss.ssid, data->length); |
415 | } |
416 | |
417 | return 0; |
418 | } |
419 | |
420 | int cfg80211_ibss_wext_siwap(struct net_device *dev, |
421 | struct iw_request_info *info, |
422 | struct sockaddr *ap_addr, char *) |
423 | { |
424 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
425 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
426 | u8 *bssid = ap_addr->sa_data; |
427 | int err; |
428 | |
429 | /* call only for ibss! */ |
430 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
431 | return -EINVAL; |
432 | |
433 | if (!rdev->ops->join_ibss) |
434 | return -EOPNOTSUPP; |
435 | |
436 | if (ap_addr->sa_family != ARPHRD_ETHER) |
437 | return -EINVAL; |
438 | |
439 | /* automatic mode */ |
440 | if (is_zero_ether_addr(addr: bssid) || is_broadcast_ether_addr(addr: bssid)) |
441 | bssid = NULL; |
442 | |
443 | if (bssid && !is_valid_ether_addr(addr: bssid)) |
444 | return -EINVAL; |
445 | |
446 | /* both automatic */ |
447 | if (!bssid && !wdev->wext.ibss.bssid) |
448 | return 0; |
449 | |
450 | /* fixed already - and no change */ |
451 | if (wdev->wext.ibss.bssid && bssid && |
452 | ether_addr_equal(addr1: bssid, addr2: wdev->wext.ibss.bssid)) |
453 | return 0; |
454 | |
455 | err = 0; |
456 | if (wdev->u.ibss.ssid_len) |
457 | err = cfg80211_leave_ibss(rdev, dev, nowext: true); |
458 | |
459 | if (err) |
460 | return err; |
461 | |
462 | if (bssid) { |
463 | memcpy(wdev->wext.bssid, bssid, ETH_ALEN); |
464 | wdev->wext.ibss.bssid = wdev->wext.bssid; |
465 | } else |
466 | wdev->wext.ibss.bssid = NULL; |
467 | |
468 | return cfg80211_ibss_wext_join(rdev, wdev); |
469 | } |
470 | |
471 | int cfg80211_ibss_wext_giwap(struct net_device *dev, |
472 | struct iw_request_info *info, |
473 | struct sockaddr *ap_addr, char *) |
474 | { |
475 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
476 | |
477 | /* call only for ibss! */ |
478 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) |
479 | return -EINVAL; |
480 | |
481 | ap_addr->sa_family = ARPHRD_ETHER; |
482 | |
483 | if (wdev->u.ibss.current_bss) |
484 | memcpy(ap_addr->sa_data, wdev->u.ibss.current_bss->pub.bssid, |
485 | ETH_ALEN); |
486 | else if (wdev->wext.ibss.bssid) |
487 | memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); |
488 | else |
489 | eth_zero_addr(addr: ap_addr->sa_data); |
490 | |
491 | return 0; |
492 | } |
493 | #endif |
494 | |