1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
204a773adSJohannes Berg /*
304a773adSJohannes Berg * Some IBSS support code for cfg80211.
404a773adSJohannes Berg *
504a773adSJohannes Berg * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
634c9a0e7SJohannes Berg * Copyright (C) 2020-2022 Intel Corporation
704a773adSJohannes Berg */
804a773adSJohannes Berg
904a773adSJohannes Berg #include <linux/etherdevice.h>
1004a773adSJohannes Berg #include <linux/if_arp.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
12bc3b2d7fSPaul Gortmaker #include <linux/export.h>
1304a773adSJohannes Berg #include <net/cfg80211.h>
140e82ffe3SJohannes Berg #include "wext-compat.h"
1504a773adSJohannes Berg #include "nl80211.h"
16e35e4d28SHila Gonen #include "rdev-ops.h"
1704a773adSJohannes Berg
1804a773adSJohannes Berg
__cfg80211_ibss_joined(struct net_device * dev,const u8 * bssid,struct ieee80211_channel * channel)19fe94f3a4SAntonio Quartulli void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
20fe94f3a4SAntonio Quartulli struct ieee80211_channel *channel)
2104a773adSJohannes Berg {
2204a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
2304a773adSJohannes Berg struct cfg80211_bss *bss;
243d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
2504a773adSJohannes Berg union iwreq_data wrqu;
2604a773adSJohannes Berg #endif
2704a773adSJohannes Berg
2804a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
2904a773adSJohannes Berg return;
3004a773adSJohannes Berg
317b0a0e3cSJohannes Berg if (!wdev->u.ibss.ssid_len)
3204a773adSJohannes Berg return;
3304a773adSJohannes Berg
34fe94f3a4SAntonio Quartulli bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
356eb18137SDedy Lansky IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
3604a773adSJohannes Berg
3704a773adSJohannes Berg if (WARN_ON(!bss))
3804a773adSJohannes Berg return;
3904a773adSJohannes Berg
407b0a0e3cSJohannes Berg if (wdev->u.ibss.current_bss) {
417b0a0e3cSJohannes Berg cfg80211_unhold_bss(wdev->u.ibss.current_bss);
427b0a0e3cSJohannes Berg cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub);
4304a773adSJohannes Berg }
4404a773adSJohannes Berg
4519957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss));
467b0a0e3cSJohannes Berg wdev->u.ibss.current_bss = bss_from_pub(bss);
4704a773adSJohannes Berg
48fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev);
49fffd0934SJohannes Berg
50f26cbf40SZhao, Gang nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
51667503ddSJohannes Berg GFP_KERNEL);
523d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
5304a773adSJohannes Berg memset(&wrqu, 0, sizeof(wrqu));
5404a773adSJohannes Berg memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
5504a773adSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
5604a773adSJohannes Berg #endif
5704a773adSJohannes Berg }
58667503ddSJohannes Berg
cfg80211_ibss_joined(struct net_device * dev,const u8 * bssid,struct ieee80211_channel * channel,gfp_t gfp)59fe94f3a4SAntonio Quartulli void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
60fe94f3a4SAntonio Quartulli struct ieee80211_channel *channel, gfp_t gfp)
61667503ddSJohannes Berg {
62667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
63f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
64667503ddSJohannes Berg struct cfg80211_event *ev;
65667503ddSJohannes Berg unsigned long flags;
66667503ddSJohannes Berg
67fe94f3a4SAntonio Quartulli trace_cfg80211_ibss_joined(dev, bssid, channel);
68fe94f3a4SAntonio Quartulli
69fe94f3a4SAntonio Quartulli if (WARN_ON(!channel))
70fe94f3a4SAntonio Quartulli return;
714ee3e063SBeni Lev
72667503ddSJohannes Berg ev = kzalloc(sizeof(*ev), gfp);
73667503ddSJohannes Berg if (!ev)
74667503ddSJohannes Berg return;
75667503ddSJohannes Berg
76667503ddSJohannes Berg ev->type = EVENT_IBSS_JOINED;
77fe94f3a4SAntonio Quartulli memcpy(ev->ij.bssid, bssid, ETH_ALEN);
78fe94f3a4SAntonio Quartulli ev->ij.channel = channel;
79667503ddSJohannes Berg
80667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags);
81667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list);
82667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags);
83e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work);
84667503ddSJohannes Berg }
8504a773adSJohannes Berg EXPORT_SYMBOL(cfg80211_ibss_joined);
8604a773adSJohannes Berg
__cfg80211_join_ibss(struct cfg80211_registered_device * rdev,struct net_device * dev,struct cfg80211_ibss_params * params,struct cfg80211_cached_keys * connkeys)87f8d16d3eSDenis Kenzior int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
8804a773adSJohannes Berg struct net_device *dev,
89fffd0934SJohannes Berg struct cfg80211_ibss_params *params,
90fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys)
9104a773adSJohannes Berg {
9204a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
9304a773adSJohannes Berg int err;
9404a773adSJohannes Berg
95a05829a7SJohannes Berg lockdep_assert_held(&rdev->wiphy.mtx);
96667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
97667503ddSJohannes Berg
987b0a0e3cSJohannes Berg if (wdev->u.ibss.ssid_len)
9904a773adSJohannes Berg return -EALREADY;
10004a773adSJohannes Berg
10193b05238SJohannes Berg if (!params->basic_rates) {
10293b05238SJohannes Berg /*
10393b05238SJohannes Berg * If no rates were explicitly configured,
10493b05238SJohannes Berg * use the mandatory rate set for 11b or
10593b05238SJohannes Berg * 11a for maximum compatibility.
10693b05238SJohannes Berg */
1075ea4e780SArend van Spriel struct ieee80211_supported_band *sband;
1085ea4e780SArend van Spriel enum nl80211_band band;
1095ea4e780SArend van Spriel u32 flag;
11093b05238SJohannes Berg int j;
11193b05238SJohannes Berg
1125ea4e780SArend van Spriel band = params->chandef.chan->band;
1135ea4e780SArend van Spriel if (band == NL80211_BAND_5GHZ ||
1145ea4e780SArend van Spriel band == NL80211_BAND_6GHZ)
1155ea4e780SArend van Spriel flag = IEEE80211_RATE_MANDATORY_A;
1165ea4e780SArend van Spriel else
1175ea4e780SArend van Spriel flag = IEEE80211_RATE_MANDATORY_B;
1185ea4e780SArend van Spriel
1195ea4e780SArend van Spriel sband = rdev->wiphy.bands[band];
12093b05238SJohannes Berg for (j = 0; j < sband->n_bitrates; j++) {
12193b05238SJohannes Berg if (sband->bitrates[j].flags & flag)
12293b05238SJohannes Berg params->basic_rates |= BIT(j);
12393b05238SJohannes Berg }
12493b05238SJohannes Berg }
12593b05238SJohannes Berg
126f1c1f17aSJohannes Berg if (WARN_ON(connkeys && connkeys->def < 0))
127f1c1f17aSJohannes Berg return -EINVAL;
128f1c1f17aSJohannes Berg
129fffd0934SJohannes Berg if (WARN_ON(wdev->connect_keys))
130453431a5SWaiman Long kfree_sensitive(wdev->connect_keys);
131fffd0934SJohannes Berg wdev->connect_keys = connkeys;
132fffd0934SJohannes Berg
1337b0a0e3cSJohannes Berg wdev->u.ibss.chandef = params->chandef;
1349ae3b172STova Mussai if (connkeys) {
1359ae3b172STova Mussai params->wep_keys = connkeys->params;
1369ae3b172STova Mussai params->wep_tx_key = connkeys->def;
1379ae3b172STova Mussai }
1389ae3b172STova Mussai
1393d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
140683b6d3bSJohannes Berg wdev->wext.ibss.chandef = params->chandef;
14104a773adSJohannes Berg #endif
142e35e4d28SHila Gonen err = rdev_join_ibss(rdev, dev, params);
143fffd0934SJohannes Berg if (err) {
144fffd0934SJohannes Berg wdev->connect_keys = NULL;
14504a773adSJohannes Berg return err;
146fffd0934SJohannes Berg }
14704a773adSJohannes Berg
1487b0a0e3cSJohannes Berg memcpy(wdev->u.ibss.ssid, params->ssid, params->ssid_len);
1497b0a0e3cSJohannes Berg wdev->u.ibss.ssid_len = params->ssid_len;
15004a773adSJohannes Berg
15104a773adSJohannes Berg return 0;
15204a773adSJohannes Berg }
15304a773adSJohannes Berg
__cfg80211_clear_ibss(struct net_device * dev,bool nowext)154667503ddSJohannes Berg static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
155667503ddSJohannes Berg {
156667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
157f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
158fffd0934SJohannes Berg int i;
159667503ddSJohannes Berg
160667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
16104a773adSJohannes Berg
162453431a5SWaiman Long kfree_sensitive(wdev->connect_keys);
163fffd0934SJohannes Berg wdev->connect_keys = NULL;
164fffd0934SJohannes Berg
165fa9ffc74SKyeyoon Park rdev_set_qos_map(rdev, dev, NULL);
166fa9ffc74SKyeyoon Park
167fffd0934SJohannes Berg /*
168fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really
169fffd0934SJohannes Berg * exist any more anyway, but default keys might.
170fffd0934SJohannes Berg */
171fffd0934SJohannes Berg if (rdev->ops->del_key)
172fffd0934SJohannes Berg for (i = 0; i < 6; i++)
173e7a7b84eSVeerendranath Jakkam rdev_del_key(rdev, dev, -1, i, false, NULL);
174fffd0934SJohannes Berg
1757b0a0e3cSJohannes Berg if (wdev->u.ibss.current_bss) {
1767b0a0e3cSJohannes Berg cfg80211_unhold_bss(wdev->u.ibss.current_bss);
1777b0a0e3cSJohannes Berg cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub);
17804a773adSJohannes Berg }
17904a773adSJohannes Berg
1807b0a0e3cSJohannes Berg wdev->u.ibss.current_bss = NULL;
1817b0a0e3cSJohannes Berg wdev->u.ibss.ssid_len = 0;
1827b0a0e3cSJohannes Berg memset(&wdev->u.ibss.chandef, 0, sizeof(wdev->u.ibss.chandef));
1833d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1849d308429SJohannes Berg if (!nowext)
185cbe8fa9cSJohannes Berg wdev->wext.ibss.ssid_len = 0;
1869d308429SJohannes Berg #endif
187b35a51c7SVasanthakumar Thiagarajan cfg80211_sched_dfs_chan_update(rdev);
18804a773adSJohannes Berg }
18904a773adSJohannes Berg
cfg80211_clear_ibss(struct net_device * dev,bool nowext)190667503ddSJohannes Berg void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
191667503ddSJohannes Berg {
192667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
193667503ddSJohannes Berg
194667503ddSJohannes Berg wdev_lock(wdev);
195667503ddSJohannes Berg __cfg80211_clear_ibss(dev, nowext);
196667503ddSJohannes Berg wdev_unlock(wdev);
197667503ddSJohannes Berg }
198667503ddSJohannes Berg
__cfg80211_leave_ibss(struct cfg80211_registered_device * rdev,struct net_device * dev,bool nowext)19998d3a7caSJohannes Berg int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
2009d308429SJohannes Berg struct net_device *dev, bool nowext)
20104a773adSJohannes Berg {
20278485475SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
20304a773adSJohannes Berg int err;
20404a773adSJohannes Berg
205667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
206667503ddSJohannes Berg
2077b0a0e3cSJohannes Berg if (!wdev->u.ibss.ssid_len)
20878485475SJohannes Berg return -ENOLINK;
20978485475SJohannes Berg
210e35e4d28SHila Gonen err = rdev_leave_ibss(rdev, dev);
21104a773adSJohannes Berg
21204a773adSJohannes Berg if (err)
21304a773adSJohannes Berg return err;
21404a773adSJohannes Berg
215f8d16d3eSDenis Kenzior wdev->conn_owner_nlportid = 0;
216667503ddSJohannes Berg __cfg80211_clear_ibss(dev, nowext);
21704a773adSJohannes Berg
21804a773adSJohannes Berg return 0;
21904a773adSJohannes Berg }
22004a773adSJohannes Berg
cfg80211_leave_ibss(struct cfg80211_registered_device * rdev,struct net_device * dev,bool nowext)221667503ddSJohannes Berg int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
222667503ddSJohannes Berg struct net_device *dev, bool nowext)
223667503ddSJohannes Berg {
224667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
225667503ddSJohannes Berg int err;
226667503ddSJohannes Berg
227667503ddSJohannes Berg wdev_lock(wdev);
228667503ddSJohannes Berg err = __cfg80211_leave_ibss(rdev, dev, nowext);
229667503ddSJohannes Berg wdev_unlock(wdev);
230667503ddSJohannes Berg
231667503ddSJohannes Berg return err;
232667503ddSJohannes Berg }
233667503ddSJohannes Berg
2343d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
cfg80211_ibss_wext_join(struct cfg80211_registered_device * rdev,struct wireless_dev * wdev)235fffd0934SJohannes Berg int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
23604a773adSJohannes Berg struct wireless_dev *wdev)
23704a773adSJohannes Berg {
238fffd0934SJohannes Berg struct cfg80211_cached_keys *ck = NULL;
23957fbcce3SJohannes Berg enum nl80211_band band;
240fffd0934SJohannes Berg int i, err;
241fffd0934SJohannes Berg
242fffd0934SJohannes Berg ASSERT_WDEV_LOCK(wdev);
24304a773adSJohannes Berg
244cbe8fa9cSJohannes Berg if (!wdev->wext.ibss.beacon_interval)
245cbe8fa9cSJohannes Berg wdev->wext.ibss.beacon_interval = 100;
2468e30bc55SJohannes Berg
24704a773adSJohannes Berg /* try to find an IBSS channel if none requested ... */
248683b6d3bSJohannes Berg if (!wdev->wext.ibss.chandef.chan) {
2491fe4517cSSimon Wunderlich struct ieee80211_channel *new_chan = NULL;
250683b6d3bSJohannes Berg
25157fbcce3SJohannes Berg for (band = 0; band < NUM_NL80211_BANDS; band++) {
25204a773adSJohannes Berg struct ieee80211_supported_band *sband;
25304a773adSJohannes Berg struct ieee80211_channel *chan;
25404a773adSJohannes Berg
25504a773adSJohannes Berg sband = rdev->wiphy.bands[band];
25604a773adSJohannes Berg if (!sband)
25704a773adSJohannes Berg continue;
25804a773adSJohannes Berg
25904a773adSJohannes Berg for (i = 0; i < sband->n_channels; i++) {
26004a773adSJohannes Berg chan = &sband->channels[i];
2618fe02e16SLuis R. Rodriguez if (chan->flags & IEEE80211_CHAN_NO_IR)
26204a773adSJohannes Berg continue;
26304a773adSJohannes Berg if (chan->flags & IEEE80211_CHAN_DISABLED)
26404a773adSJohannes Berg continue;
2651fe4517cSSimon Wunderlich new_chan = chan;
26604a773adSJohannes Berg break;
26704a773adSJohannes Berg }
26804a773adSJohannes Berg
2691fe4517cSSimon Wunderlich if (new_chan)
27004a773adSJohannes Berg break;
27104a773adSJohannes Berg }
27204a773adSJohannes Berg
2731fe4517cSSimon Wunderlich if (!new_chan)
27404a773adSJohannes Berg return -EINVAL;
2751fe4517cSSimon Wunderlich
2761fe4517cSSimon Wunderlich cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan,
2771fe4517cSSimon Wunderlich NL80211_CHAN_NO_HT);
27804a773adSJohannes Berg }
27904a773adSJohannes Berg
28004a773adSJohannes Berg /* don't join -- SSID is not there */
281cbe8fa9cSJohannes Berg if (!wdev->wext.ibss.ssid_len)
28204a773adSJohannes Berg return 0;
28304a773adSJohannes Berg
28404a773adSJohannes Berg if (!netif_running(wdev->netdev))
28504a773adSJohannes Berg return 0;
28604a773adSJohannes Berg
28789b706fbSJohannes Berg if (wdev->wext.keys)
288fffd0934SJohannes Berg wdev->wext.keys->def = wdev->wext.default_key;
289fffd0934SJohannes Berg
290fffd0934SJohannes Berg wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
291fffd0934SJohannes Berg
292f1c1f17aSJohannes Berg if (wdev->wext.keys && wdev->wext.keys->def != -1) {
293fffd0934SJohannes Berg ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
294fffd0934SJohannes Berg if (!ck)
295fffd0934SJohannes Berg return -ENOMEM;
296*585b6e13SJohannes Berg for (i = 0; i < 4; i++)
297fffd0934SJohannes Berg ck->params[i].key = ck->data[i];
298fffd0934SJohannes Berg }
299fffd0934SJohannes Berg err = __cfg80211_join_ibss(rdev, wdev->netdev,
300fffd0934SJohannes Berg &wdev->wext.ibss, ck);
301fffd0934SJohannes Berg if (err)
302fffd0934SJohannes Berg kfree(ck);
303fffd0934SJohannes Berg
304fffd0934SJohannes Berg return err;
30504a773adSJohannes Berg }
30604a773adSJohannes Berg
cfg80211_ibss_wext_siwfreq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * wextfreq,char * extra)30704a773adSJohannes Berg int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
30804a773adSJohannes Berg struct iw_request_info *info,
30959bbb6f7SJohannes Berg struct iw_freq *wextfreq, char *extra)
31004a773adSJohannes Berg {
31104a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
312f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
31359bbb6f7SJohannes Berg struct ieee80211_channel *chan = NULL;
31459bbb6f7SJohannes Berg int err, freq;
31504a773adSJohannes Berg
31604a773adSJohannes Berg /* call only for ibss! */
31704a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
31804a773adSJohannes Berg return -EINVAL;
31904a773adSJohannes Berg
32059bbb6f7SJohannes Berg if (!rdev->ops->join_ibss)
32104a773adSJohannes Berg return -EOPNOTSUPP;
32204a773adSJohannes Berg
32396998e3aSZhao, Gang freq = cfg80211_wext_freq(wextfreq);
32459bbb6f7SJohannes Berg if (freq < 0)
32559bbb6f7SJohannes Berg return freq;
32604a773adSJohannes Berg
32759bbb6f7SJohannes Berg if (freq) {
32859bbb6f7SJohannes Berg chan = ieee80211_get_channel(wdev->wiphy, freq);
32959bbb6f7SJohannes Berg if (!chan)
33004a773adSJohannes Berg return -EINVAL;
3318fe02e16SLuis R. Rodriguez if (chan->flags & IEEE80211_CHAN_NO_IR ||
33259bbb6f7SJohannes Berg chan->flags & IEEE80211_CHAN_DISABLED)
33359bbb6f7SJohannes Berg return -EINVAL;
33459bbb6f7SJohannes Berg }
33504a773adSJohannes Berg
336683b6d3bSJohannes Berg if (wdev->wext.ibss.chandef.chan == chan)
33704a773adSJohannes Berg return 0;
33804a773adSJohannes Berg
339667503ddSJohannes Berg wdev_lock(wdev);
340667503ddSJohannes Berg err = 0;
3417b0a0e3cSJohannes Berg if (wdev->u.ibss.ssid_len)
34259bbb6f7SJohannes Berg err = __cfg80211_leave_ibss(rdev, dev, true);
343667503ddSJohannes Berg wdev_unlock(wdev);
344667503ddSJohannes Berg
34504a773adSJohannes Berg if (err)
34604a773adSJohannes Berg return err;
34704a773adSJohannes Berg
34804a773adSJohannes Berg if (chan) {
3491fe4517cSSimon Wunderlich cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan,
3501fe4517cSSimon Wunderlich NL80211_CHAN_NO_HT);
351cbe8fa9cSJohannes Berg wdev->wext.ibss.channel_fixed = true;
35204a773adSJohannes Berg } else {
35304a773adSJohannes Berg /* cfg80211_ibss_wext_join will pick one if needed */
354cbe8fa9cSJohannes Berg wdev->wext.ibss.channel_fixed = false;
35504a773adSJohannes Berg }
35604a773adSJohannes Berg
357fffd0934SJohannes Berg wdev_lock(wdev);
35859bbb6f7SJohannes Berg err = cfg80211_ibss_wext_join(rdev, wdev);
359fffd0934SJohannes Berg wdev_unlock(wdev);
360fffd0934SJohannes Berg
361fffd0934SJohannes Berg return err;
36204a773adSJohannes Berg }
36304a773adSJohannes Berg
cfg80211_ibss_wext_giwfreq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * freq,char * extra)36404a773adSJohannes Berg int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
36504a773adSJohannes Berg struct iw_request_info *info,
36604a773adSJohannes Berg struct iw_freq *freq, char *extra)
36704a773adSJohannes Berg {
36804a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
36904a773adSJohannes Berg struct ieee80211_channel *chan = NULL;
37004a773adSJohannes Berg
37104a773adSJohannes Berg /* call only for ibss! */
37204a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
37304a773adSJohannes Berg return -EINVAL;
37404a773adSJohannes Berg
375667503ddSJohannes Berg wdev_lock(wdev);
3767b0a0e3cSJohannes Berg if (wdev->u.ibss.current_bss)
3777b0a0e3cSJohannes Berg chan = wdev->u.ibss.current_bss->pub.channel;
378683b6d3bSJohannes Berg else if (wdev->wext.ibss.chandef.chan)
379683b6d3bSJohannes Berg chan = wdev->wext.ibss.chandef.chan;
380667503ddSJohannes Berg wdev_unlock(wdev);
38104a773adSJohannes Berg
38204a773adSJohannes Berg if (chan) {
38304a773adSJohannes Berg freq->m = chan->center_freq;
38404a773adSJohannes Berg freq->e = 6;
38504a773adSJohannes Berg return 0;
38604a773adSJohannes Berg }
38704a773adSJohannes Berg
38804a773adSJohannes Berg /* no channel if not joining */
38904a773adSJohannes Berg return -EINVAL;
39004a773adSJohannes Berg }
39104a773adSJohannes Berg
cfg80211_ibss_wext_siwessid(struct net_device * dev,struct iw_request_info * info,struct iw_point * data,char * ssid)39204a773adSJohannes Berg int cfg80211_ibss_wext_siwessid(struct net_device *dev,
39304a773adSJohannes Berg struct iw_request_info *info,
39404a773adSJohannes Berg struct iw_point *data, char *ssid)
39504a773adSJohannes Berg {
39604a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
397f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
39804a773adSJohannes Berg size_t len = data->length;
39904a773adSJohannes Berg int err;
40004a773adSJohannes Berg
40104a773adSJohannes Berg /* call only for ibss! */
40204a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
40304a773adSJohannes Berg return -EINVAL;
40404a773adSJohannes Berg
40559bbb6f7SJohannes Berg if (!rdev->ops->join_ibss)
40604a773adSJohannes Berg return -EOPNOTSUPP;
40704a773adSJohannes Berg
408667503ddSJohannes Berg wdev_lock(wdev);
409667503ddSJohannes Berg err = 0;
4107b0a0e3cSJohannes Berg if (wdev->u.ibss.ssid_len)
41159bbb6f7SJohannes Berg err = __cfg80211_leave_ibss(rdev, dev, true);
412667503ddSJohannes Berg wdev_unlock(wdev);
413667503ddSJohannes Berg
41404a773adSJohannes Berg if (err)
41504a773adSJohannes Berg return err;
41604a773adSJohannes Berg
41704a773adSJohannes Berg /* iwconfig uses nul termination in SSID.. */
41804a773adSJohannes Berg if (len > 0 && ssid[len - 1] == '\0')
41904a773adSJohannes Berg len--;
42004a773adSJohannes Berg
4217b0a0e3cSJohannes Berg memcpy(wdev->u.ibss.ssid, ssid, len);
4227b0a0e3cSJohannes Berg wdev->wext.ibss.ssid = wdev->u.ibss.ssid;
423cbe8fa9cSJohannes Berg wdev->wext.ibss.ssid_len = len;
42404a773adSJohannes Berg
425fffd0934SJohannes Berg wdev_lock(wdev);
42659bbb6f7SJohannes Berg err = cfg80211_ibss_wext_join(rdev, wdev);
427fffd0934SJohannes Berg wdev_unlock(wdev);
428fffd0934SJohannes Berg
429fffd0934SJohannes Berg return err;
43004a773adSJohannes Berg }
43104a773adSJohannes Berg
cfg80211_ibss_wext_giwessid(struct net_device * dev,struct iw_request_info * info,struct iw_point * data,char * ssid)43204a773adSJohannes Berg int cfg80211_ibss_wext_giwessid(struct net_device *dev,
43304a773adSJohannes Berg struct iw_request_info *info,
43404a773adSJohannes Berg struct iw_point *data, char *ssid)
43504a773adSJohannes Berg {
43604a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
43704a773adSJohannes Berg
43804a773adSJohannes Berg /* call only for ibss! */
43904a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
44004a773adSJohannes Berg return -EINVAL;
44104a773adSJohannes Berg
44204a773adSJohannes Berg data->flags = 0;
44304a773adSJohannes Berg
444667503ddSJohannes Berg wdev_lock(wdev);
4457b0a0e3cSJohannes Berg if (wdev->u.ibss.ssid_len) {
44604a773adSJohannes Berg data->flags = 1;
4477b0a0e3cSJohannes Berg data->length = wdev->u.ibss.ssid_len;
4487b0a0e3cSJohannes Berg memcpy(ssid, wdev->u.ibss.ssid, data->length);
449cbe8fa9cSJohannes Berg } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
45004a773adSJohannes Berg data->flags = 1;
451cbe8fa9cSJohannes Berg data->length = wdev->wext.ibss.ssid_len;
452cbe8fa9cSJohannes Berg memcpy(ssid, wdev->wext.ibss.ssid, data->length);
45304a773adSJohannes Berg }
454667503ddSJohannes Berg wdev_unlock(wdev);
45504a773adSJohannes Berg
45604a773adSJohannes Berg return 0;
45704a773adSJohannes Berg }
45804a773adSJohannes Berg
cfg80211_ibss_wext_siwap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * ap_addr,char * extra)45904a773adSJohannes Berg int cfg80211_ibss_wext_siwap(struct net_device *dev,
46004a773adSJohannes Berg struct iw_request_info *info,
46104a773adSJohannes Berg struct sockaddr *ap_addr, char *extra)
46204a773adSJohannes Berg {
46304a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
464f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
46504a773adSJohannes Berg u8 *bssid = ap_addr->sa_data;
46604a773adSJohannes Berg int err;
46704a773adSJohannes Berg
46804a773adSJohannes Berg /* call only for ibss! */
46904a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
47004a773adSJohannes Berg return -EINVAL;
47104a773adSJohannes Berg
47259bbb6f7SJohannes Berg if (!rdev->ops->join_ibss)
47304a773adSJohannes Berg return -EOPNOTSUPP;
47404a773adSJohannes Berg
47504a773adSJohannes Berg if (ap_addr->sa_family != ARPHRD_ETHER)
47604a773adSJohannes Berg return -EINVAL;
47704a773adSJohannes Berg
47804a773adSJohannes Berg /* automatic mode */
47904a773adSJohannes Berg if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
48004a773adSJohannes Berg bssid = NULL;
48104a773adSJohannes Berg
48274f82741SJohannes Berg if (bssid && !is_valid_ether_addr(bssid))
48374f82741SJohannes Berg return -EINVAL;
48474f82741SJohannes Berg
48504a773adSJohannes Berg /* both automatic */
486cbe8fa9cSJohannes Berg if (!bssid && !wdev->wext.ibss.bssid)
48704a773adSJohannes Berg return 0;
48804a773adSJohannes Berg
48904a773adSJohannes Berg /* fixed already - and no change */
490cbe8fa9cSJohannes Berg if (wdev->wext.ibss.bssid && bssid &&
491ac422d3cSJoe Perches ether_addr_equal(bssid, wdev->wext.ibss.bssid))
49204a773adSJohannes Berg return 0;
49304a773adSJohannes Berg
494667503ddSJohannes Berg wdev_lock(wdev);
495667503ddSJohannes Berg err = 0;
4967b0a0e3cSJohannes Berg if (wdev->u.ibss.ssid_len)
49759bbb6f7SJohannes Berg err = __cfg80211_leave_ibss(rdev, dev, true);
498667503ddSJohannes Berg wdev_unlock(wdev);
499667503ddSJohannes Berg
50004a773adSJohannes Berg if (err)
50104a773adSJohannes Berg return err;
50204a773adSJohannes Berg
50304a773adSJohannes Berg if (bssid) {
504cbe8fa9cSJohannes Berg memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
505cbe8fa9cSJohannes Berg wdev->wext.ibss.bssid = wdev->wext.bssid;
50604a773adSJohannes Berg } else
507cbe8fa9cSJohannes Berg wdev->wext.ibss.bssid = NULL;
50804a773adSJohannes Berg
509fffd0934SJohannes Berg wdev_lock(wdev);
51059bbb6f7SJohannes Berg err = cfg80211_ibss_wext_join(rdev, wdev);
511fffd0934SJohannes Berg wdev_unlock(wdev);
512fffd0934SJohannes Berg
513fffd0934SJohannes Berg return err;
51404a773adSJohannes Berg }
51504a773adSJohannes Berg
cfg80211_ibss_wext_giwap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * ap_addr,char * extra)51604a773adSJohannes Berg int cfg80211_ibss_wext_giwap(struct net_device *dev,
51704a773adSJohannes Berg struct iw_request_info *info,
51804a773adSJohannes Berg struct sockaddr *ap_addr, char *extra)
51904a773adSJohannes Berg {
52004a773adSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
52104a773adSJohannes Berg
52204a773adSJohannes Berg /* call only for ibss! */
52304a773adSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
52404a773adSJohannes Berg return -EINVAL;
52504a773adSJohannes Berg
52604a773adSJohannes Berg ap_addr->sa_family = ARPHRD_ETHER;
52704a773adSJohannes Berg
528667503ddSJohannes Berg wdev_lock(wdev);
5297b0a0e3cSJohannes Berg if (wdev->u.ibss.current_bss)
5307b0a0e3cSJohannes Berg memcpy(ap_addr->sa_data, wdev->u.ibss.current_bss->pub.bssid,
5317b0a0e3cSJohannes Berg ETH_ALEN);
53280e5b06aSZhu Yi else if (wdev->wext.ibss.bssid)
533cbe8fa9cSJohannes Berg memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
53480e5b06aSZhu Yi else
535d2beae10SJoe Perches eth_zero_addr(ap_addr->sa_data);
53680e5b06aSZhu Yi
537667503ddSJohannes Berg wdev_unlock(wdev);
538667503ddSJohannes Berg
53904a773adSJohannes Berg return 0;
54004a773adSJohannes Berg }
54104a773adSJohannes Berg #endif
542