ibss.c revision 8e30bc55de98c000b0b836cb42525c82f605f191
1/*
2 * Some IBSS support code for cfg80211.
3 *
4 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
5 */
6
7#include <linux/etherdevice.h>
8#include <linux/if_arp.h>
9#include <net/cfg80211.h>
10#include "nl80211.h"
11
12
13void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
14{
15	struct wireless_dev *wdev = dev->ieee80211_ptr;
16	struct cfg80211_bss *bss;
17#ifdef CONFIG_WIRELESS_EXT
18	union iwreq_data wrqu;
19#endif
20
21	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
22		return;
23
24	if (WARN_ON(!wdev->ssid_len))
25		return;
26
27	if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0)
28		return;
29
30	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
31			       wdev->ssid, wdev->ssid_len,
32			       WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
33
34	if (WARN_ON(!bss))
35		return;
36
37	if (wdev->current_bss) {
38		cfg80211_unhold_bss(wdev->current_bss);
39		cfg80211_put_bss(wdev->current_bss);
40	}
41
42	cfg80211_hold_bss(bss);
43	wdev->current_bss = bss;
44	memcpy(wdev->bssid, bssid, ETH_ALEN);
45
46	nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp);
47#ifdef CONFIG_WIRELESS_EXT
48	memset(&wrqu, 0, sizeof(wrqu));
49	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
50	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
51#endif
52}
53EXPORT_SYMBOL(cfg80211_ibss_joined);
54
55int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
56		       struct net_device *dev,
57		       struct cfg80211_ibss_params *params)
58{
59	struct wireless_dev *wdev = dev->ieee80211_ptr;
60	int err;
61
62	if (wdev->ssid_len)
63		return -EALREADY;
64
65#ifdef CONFIG_WIRELESS_EXT
66	wdev->wext.channel = params->channel;
67#endif
68	err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
69
70	if (err)
71		return err;
72
73	memcpy(wdev->ssid, params->ssid, params->ssid_len);
74	wdev->ssid_len = params->ssid_len;
75
76	return 0;
77}
78
79void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
80{
81	struct wireless_dev *wdev = dev->ieee80211_ptr;
82
83	if (wdev->current_bss) {
84		cfg80211_unhold_bss(wdev->current_bss);
85		cfg80211_put_bss(wdev->current_bss);
86	}
87
88	wdev->current_bss = NULL;
89	wdev->ssid_len = 0;
90	memset(wdev->bssid, 0, ETH_ALEN);
91#ifdef CONFIG_WIRELESS_EXT
92	if (!nowext)
93		wdev->wext.ssid_len = 0;
94#endif
95}
96
97int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
98			struct net_device *dev, bool nowext)
99{
100	int err;
101
102	err = rdev->ops->leave_ibss(&rdev->wiphy, dev);
103
104	if (err)
105		return err;
106
107	cfg80211_clear_ibss(dev, nowext);
108
109	return 0;
110}
111
112#ifdef CONFIG_WIRELESS_EXT
113static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
114				   struct wireless_dev *wdev)
115{
116	enum ieee80211_band band;
117	int i;
118
119	if (!wdev->wext.beacon_interval)
120		wdev->wext.beacon_interval = 100;
121
122	/* try to find an IBSS channel if none requested ... */
123	if (!wdev->wext.channel) {
124		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
125			struct ieee80211_supported_band *sband;
126			struct ieee80211_channel *chan;
127
128			sband = rdev->wiphy.bands[band];
129			if (!sband)
130				continue;
131
132			for (i = 0; i < sband->n_channels; i++) {
133				chan = &sband->channels[i];
134				if (chan->flags & IEEE80211_CHAN_NO_IBSS)
135					continue;
136				if (chan->flags & IEEE80211_CHAN_DISABLED)
137					continue;
138				wdev->wext.channel = chan;
139				break;
140			}
141
142			if (wdev->wext.channel)
143				break;
144		}
145
146		if (!wdev->wext.channel)
147			return -EINVAL;
148	}
149
150	/* don't join -- SSID is not there */
151	if (!wdev->wext.ssid_len)
152		return 0;
153
154	if (!netif_running(wdev->netdev))
155		return 0;
156
157	return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy),
158				  wdev->netdev, &wdev->wext);
159}
160
161int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
162			       struct iw_request_info *info,
163			       struct iw_freq *freq, char *extra)
164{
165	struct wireless_dev *wdev = dev->ieee80211_ptr;
166	struct ieee80211_channel *chan;
167	int err;
168
169	/* call only for ibss! */
170	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
171		return -EINVAL;
172
173	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
174		return -EOPNOTSUPP;
175
176	chan = cfg80211_wext_freq(wdev->wiphy, freq);
177	if (chan && IS_ERR(chan))
178		return PTR_ERR(chan);
179
180	if (chan &&
181	    (chan->flags & IEEE80211_CHAN_NO_IBSS ||
182	     chan->flags & IEEE80211_CHAN_DISABLED))
183		return -EINVAL;
184
185	if (wdev->wext.channel == chan)
186		return 0;
187
188	if (wdev->ssid_len) {
189		err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
190					  dev, true);
191		if (err)
192			return err;
193	}
194
195	if (chan) {
196		wdev->wext.channel = chan;
197		wdev->wext.channel_fixed = true;
198	} else {
199		/* cfg80211_ibss_wext_join will pick one if needed */
200		wdev->wext.channel_fixed = false;
201	}
202
203	return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
204}
205/* temporary symbol - mark GPL - in the future the handler won't be */
206EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq);
207
208int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
209			       struct iw_request_info *info,
210			       struct iw_freq *freq, char *extra)
211{
212	struct wireless_dev *wdev = dev->ieee80211_ptr;
213	struct ieee80211_channel *chan = NULL;
214
215	/* call only for ibss! */
216	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
217		return -EINVAL;
218
219	if (wdev->current_bss)
220		chan = wdev->current_bss->channel;
221	else if (wdev->wext.channel)
222		chan = wdev->wext.channel;
223
224	if (chan) {
225		freq->m = chan->center_freq;
226		freq->e = 6;
227		return 0;
228	}
229
230	/* no channel if not joining */
231	return -EINVAL;
232}
233/* temporary symbol - mark GPL - in the future the handler won't be */
234EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq);
235
236int cfg80211_ibss_wext_siwessid(struct net_device *dev,
237				struct iw_request_info *info,
238				struct iw_point *data, char *ssid)
239{
240	struct wireless_dev *wdev = dev->ieee80211_ptr;
241	size_t len = data->length;
242	int err;
243
244	/* call only for ibss! */
245	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
246		return -EINVAL;
247
248	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
249		return -EOPNOTSUPP;
250
251	if (wdev->ssid_len) {
252		err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
253					  dev, true);
254		if (err)
255			return err;
256	}
257
258	/* iwconfig uses nul termination in SSID.. */
259	if (len > 0 && ssid[len - 1] == '\0')
260		len--;
261
262	wdev->wext.ssid = wdev->ssid;
263	memcpy(wdev->wext.ssid, ssid, len);
264	wdev->wext.ssid_len = len;
265
266	return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
267}
268/* temporary symbol - mark GPL - in the future the handler won't be */
269EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);
270
271int cfg80211_ibss_wext_giwessid(struct net_device *dev,
272				struct iw_request_info *info,
273				struct iw_point *data, char *ssid)
274{
275	struct wireless_dev *wdev = dev->ieee80211_ptr;
276
277	/* call only for ibss! */
278	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
279		return -EINVAL;
280
281	data->flags = 0;
282
283	if (wdev->ssid_len) {
284		data->flags = 1;
285		data->length = wdev->ssid_len;
286		memcpy(ssid, wdev->ssid, data->length);
287	} else if (wdev->wext.ssid && wdev->wext.ssid_len) {
288		data->flags = 1;
289		data->length = wdev->wext.ssid_len;
290		memcpy(ssid, wdev->wext.ssid, data->length);
291	}
292
293	return 0;
294}
295/* temporary symbol - mark GPL - in the future the handler won't be */
296EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);
297
298int cfg80211_ibss_wext_siwap(struct net_device *dev,
299			     struct iw_request_info *info,
300			     struct sockaddr *ap_addr, char *extra)
301{
302	struct wireless_dev *wdev = dev->ieee80211_ptr;
303	u8 *bssid = ap_addr->sa_data;
304	int err;
305
306	/* call only for ibss! */
307	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
308		return -EINVAL;
309
310	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
311		return -EOPNOTSUPP;
312
313	if (ap_addr->sa_family != ARPHRD_ETHER)
314		return -EINVAL;
315
316	/* automatic mode */
317	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
318		bssid = NULL;
319
320	/* both automatic */
321	if (!bssid && !wdev->wext.bssid)
322		return 0;
323
324	/* fixed already - and no change */
325	if (wdev->wext.bssid && bssid &&
326	    compare_ether_addr(bssid, wdev->wext.bssid) == 0)
327		return 0;
328
329	if (wdev->ssid_len) {
330		err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
331					  dev, true);
332		if (err)
333			return err;
334	}
335
336	if (bssid) {
337		memcpy(wdev->wext_bssid, bssid, ETH_ALEN);
338		wdev->wext.bssid = wdev->wext_bssid;
339	} else
340		wdev->wext.bssid = NULL;
341
342	return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
343}
344/* temporary symbol - mark GPL - in the future the handler won't be */
345EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap);
346
347int cfg80211_ibss_wext_giwap(struct net_device *dev,
348			     struct iw_request_info *info,
349			     struct sockaddr *ap_addr, char *extra)
350{
351	struct wireless_dev *wdev = dev->ieee80211_ptr;
352
353	/* call only for ibss! */
354	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
355		return -EINVAL;
356
357	ap_addr->sa_family = ARPHRD_ETHER;
358
359	if (wdev->wext.bssid) {
360		memcpy(ap_addr->sa_data, wdev->wext.bssid, ETH_ALEN);
361		return 0;
362	}
363
364	memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN);
365	return 0;
366}
367/* temporary symbol - mark GPL - in the future the handler won't be */
368EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap);
369#endif
370