ibss.c revision 667503ddcb96f3b10211f997fe55907fa7509841
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)
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	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
28			       wdev->ssid, wdev->ssid_len,
29			       WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
30
31	if (WARN_ON(!bss))
32		return;
33
34	if (wdev->current_bss) {
35		cfg80211_unhold_bss(wdev->current_bss);
36		cfg80211_put_bss(&wdev->current_bss->pub);
37	}
38
39	cfg80211_hold_bss(bss_from_pub(bss));
40	wdev->current_bss = bss_from_pub(bss);
41
42	nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
43				GFP_KERNEL);
44#ifdef CONFIG_WIRELESS_EXT
45	memset(&wrqu, 0, sizeof(wrqu));
46	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
47	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
48#endif
49}
50
51void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
52{
53	struct wireless_dev *wdev = dev->ieee80211_ptr;
54	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
55	struct cfg80211_event *ev;
56	unsigned long flags;
57
58	ev = kzalloc(sizeof(*ev), gfp);
59	if (!ev)
60		return;
61
62	ev->type = EVENT_IBSS_JOINED;
63	memcpy(ev->cr.bssid, bssid, ETH_ALEN);
64
65	spin_lock_irqsave(&wdev->event_lock, flags);
66	list_add_tail(&ev->list, &wdev->event_list);
67	spin_unlock_irqrestore(&wdev->event_lock, flags);
68	schedule_work(&rdev->event_work);
69}
70EXPORT_SYMBOL(cfg80211_ibss_joined);
71
72int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
73			 struct net_device *dev,
74			 struct cfg80211_ibss_params *params)
75{
76	struct wireless_dev *wdev = dev->ieee80211_ptr;
77	int err;
78
79	ASSERT_WDEV_LOCK(wdev);
80
81	if (wdev->ssid_len)
82		return -EALREADY;
83
84#ifdef CONFIG_WIRELESS_EXT
85	wdev->wext.ibss.channel = params->channel;
86#endif
87	err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
88
89	if (err)
90		return err;
91
92	memcpy(wdev->ssid, params->ssid, params->ssid_len);
93	wdev->ssid_len = params->ssid_len;
94
95	return 0;
96}
97
98int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
99		       struct net_device *dev,
100		       struct cfg80211_ibss_params *params)
101{
102	struct wireless_dev *wdev = dev->ieee80211_ptr;
103	int err;
104
105	wdev_lock(wdev);
106	err = __cfg80211_join_ibss(rdev, dev, params);
107	wdev_unlock(wdev);
108
109	return err;
110}
111
112static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
113{
114	struct wireless_dev *wdev = dev->ieee80211_ptr;
115
116	ASSERT_WDEV_LOCK(wdev);
117
118	if (wdev->current_bss) {
119		cfg80211_unhold_bss(wdev->current_bss);
120		cfg80211_put_bss(&wdev->current_bss->pub);
121	}
122
123	wdev->current_bss = NULL;
124	wdev->ssid_len = 0;
125#ifdef CONFIG_WIRELESS_EXT
126	if (!nowext)
127		wdev->wext.ibss.ssid_len = 0;
128#endif
129}
130
131void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
132{
133	struct wireless_dev *wdev = dev->ieee80211_ptr;
134
135	wdev_lock(wdev);
136	__cfg80211_clear_ibss(dev, nowext);
137	wdev_unlock(wdev);
138}
139
140static int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
141				 struct net_device *dev, bool nowext)
142{
143	struct wireless_dev *wdev = dev->ieee80211_ptr;
144	int err;
145
146	ASSERT_WDEV_LOCK(wdev);
147
148	if (!wdev->ssid_len)
149		return -ENOLINK;
150
151	err = rdev->ops->leave_ibss(&rdev->wiphy, dev);
152
153	if (err)
154		return err;
155
156	__cfg80211_clear_ibss(dev, nowext);
157
158	return 0;
159}
160
161int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
162			struct net_device *dev, bool nowext)
163{
164	struct wireless_dev *wdev = dev->ieee80211_ptr;
165	int err;
166
167	wdev_lock(wdev);
168	err = __cfg80211_leave_ibss(rdev, dev, nowext);
169	wdev_unlock(wdev);
170
171	return err;
172}
173
174#ifdef CONFIG_WIRELESS_EXT
175static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
176				   struct wireless_dev *wdev)
177{
178	enum ieee80211_band band;
179	int i;
180
181	if (!wdev->wext.ibss.beacon_interval)
182		wdev->wext.ibss.beacon_interval = 100;
183
184	/* try to find an IBSS channel if none requested ... */
185	if (!wdev->wext.ibss.channel) {
186		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
187			struct ieee80211_supported_band *sband;
188			struct ieee80211_channel *chan;
189
190			sband = rdev->wiphy.bands[band];
191			if (!sband)
192				continue;
193
194			for (i = 0; i < sband->n_channels; i++) {
195				chan = &sband->channels[i];
196				if (chan->flags & IEEE80211_CHAN_NO_IBSS)
197					continue;
198				if (chan->flags & IEEE80211_CHAN_DISABLED)
199					continue;
200				wdev->wext.ibss.channel = chan;
201				break;
202			}
203
204			if (wdev->wext.ibss.channel)
205				break;
206		}
207
208		if (!wdev->wext.ibss.channel)
209			return -EINVAL;
210	}
211
212	/* don't join -- SSID is not there */
213	if (!wdev->wext.ibss.ssid_len)
214		return 0;
215
216	if (!netif_running(wdev->netdev))
217		return 0;
218
219	return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy),
220				  wdev->netdev, &wdev->wext.ibss);
221}
222
223int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
224			       struct iw_request_info *info,
225			       struct iw_freq *freq, char *extra)
226{
227	struct wireless_dev *wdev = dev->ieee80211_ptr;
228	struct ieee80211_channel *chan;
229	int err;
230
231	/* call only for ibss! */
232	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
233		return -EINVAL;
234
235	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
236		return -EOPNOTSUPP;
237
238	chan = cfg80211_wext_freq(wdev->wiphy, freq);
239	if (chan && IS_ERR(chan))
240		return PTR_ERR(chan);
241
242	if (chan &&
243	    (chan->flags & IEEE80211_CHAN_NO_IBSS ||
244	     chan->flags & IEEE80211_CHAN_DISABLED))
245		return -EINVAL;
246
247	if (wdev->wext.ibss.channel == chan)
248		return 0;
249
250	wdev_lock(wdev);
251	err = 0;
252	if (wdev->ssid_len)
253		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
254					    dev, true);
255	wdev_unlock(wdev);
256
257	if (err)
258		return err;
259
260	if (chan) {
261		wdev->wext.ibss.channel = chan;
262		wdev->wext.ibss.channel_fixed = true;
263	} else {
264		/* cfg80211_ibss_wext_join will pick one if needed */
265		wdev->wext.ibss.channel_fixed = false;
266	}
267
268	return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
269}
270/* temporary symbol - mark GPL - in the future the handler won't be */
271EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq);
272
273int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
274			       struct iw_request_info *info,
275			       struct iw_freq *freq, char *extra)
276{
277	struct wireless_dev *wdev = dev->ieee80211_ptr;
278	struct ieee80211_channel *chan = NULL;
279
280	/* call only for ibss! */
281	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
282		return -EINVAL;
283
284	wdev_lock(wdev);
285	if (wdev->current_bss)
286		chan = wdev->current_bss->pub.channel;
287	else if (wdev->wext.ibss.channel)
288		chan = wdev->wext.ibss.channel;
289	wdev_unlock(wdev);
290
291	if (chan) {
292		freq->m = chan->center_freq;
293		freq->e = 6;
294		return 0;
295	}
296
297	/* no channel if not joining */
298	return -EINVAL;
299}
300/* temporary symbol - mark GPL - in the future the handler won't be */
301EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq);
302
303int cfg80211_ibss_wext_siwessid(struct net_device *dev,
304				struct iw_request_info *info,
305				struct iw_point *data, char *ssid)
306{
307	struct wireless_dev *wdev = dev->ieee80211_ptr;
308	size_t len = data->length;
309	int err;
310
311	/* call only for ibss! */
312	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
313		return -EINVAL;
314
315	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
316		return -EOPNOTSUPP;
317
318	wdev_lock(wdev);
319	err = 0;
320	if (wdev->ssid_len)
321		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
322					    dev, true);
323	wdev_unlock(wdev);
324
325	if (err)
326		return err;
327
328	/* iwconfig uses nul termination in SSID.. */
329	if (len > 0 && ssid[len - 1] == '\0')
330		len--;
331
332	wdev->wext.ibss.ssid = wdev->ssid;
333	memcpy(wdev->wext.ibss.ssid, ssid, len);
334	wdev->wext.ibss.ssid_len = len;
335
336	return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
337}
338/* temporary symbol - mark GPL - in the future the handler won't be */
339EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);
340
341int cfg80211_ibss_wext_giwessid(struct net_device *dev,
342				struct iw_request_info *info,
343				struct iw_point *data, char *ssid)
344{
345	struct wireless_dev *wdev = dev->ieee80211_ptr;
346
347	/* call only for ibss! */
348	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
349		return -EINVAL;
350
351	data->flags = 0;
352
353	wdev_lock(wdev);
354	if (wdev->ssid_len) {
355		data->flags = 1;
356		data->length = wdev->ssid_len;
357		memcpy(ssid, wdev->ssid, data->length);
358	} else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
359		data->flags = 1;
360		data->length = wdev->wext.ibss.ssid_len;
361		memcpy(ssid, wdev->wext.ibss.ssid, data->length);
362	}
363	wdev_unlock(wdev);
364
365	return 0;
366}
367/* temporary symbol - mark GPL - in the future the handler won't be */
368EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);
369
370int cfg80211_ibss_wext_siwap(struct net_device *dev,
371			     struct iw_request_info *info,
372			     struct sockaddr *ap_addr, char *extra)
373{
374	struct wireless_dev *wdev = dev->ieee80211_ptr;
375	u8 *bssid = ap_addr->sa_data;
376	int err;
377
378	/* call only for ibss! */
379	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
380		return -EINVAL;
381
382	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
383		return -EOPNOTSUPP;
384
385	if (ap_addr->sa_family != ARPHRD_ETHER)
386		return -EINVAL;
387
388	/* automatic mode */
389	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
390		bssid = NULL;
391
392	/* both automatic */
393	if (!bssid && !wdev->wext.ibss.bssid)
394		return 0;
395
396	/* fixed already - and no change */
397	if (wdev->wext.ibss.bssid && bssid &&
398	    compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0)
399		return 0;
400
401	wdev_lock(wdev);
402	err = 0;
403	if (wdev->ssid_len)
404		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
405					    dev, true);
406	wdev_unlock(wdev);
407
408	if (err)
409		return err;
410
411	if (bssid) {
412		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
413		wdev->wext.ibss.bssid = wdev->wext.bssid;
414	} else
415		wdev->wext.ibss.bssid = NULL;
416
417	return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
418}
419/* temporary symbol - mark GPL - in the future the handler won't be */
420EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap);
421
422int cfg80211_ibss_wext_giwap(struct net_device *dev,
423			     struct iw_request_info *info,
424			     struct sockaddr *ap_addr, char *extra)
425{
426	struct wireless_dev *wdev = dev->ieee80211_ptr;
427
428	/* call only for ibss! */
429	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
430		return -EINVAL;
431
432	ap_addr->sa_family = ARPHRD_ETHER;
433
434	wdev_lock(wdev);
435	if (wdev->current_bss)
436		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
437	else
438		memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
439	wdev_unlock(wdev);
440
441	return 0;
442}
443/* temporary symbol - mark GPL - in the future the handler won't be */
444EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap);
445#endif
446