aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-04-19 15:24:32 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-04-22 16:57:17 -0400
commit04a773ade0680d862b479d7219973df60f7a3834 (patch)
treefc759eb79099fefd7f1329bcb2b703008cb0adfe /net/wireless
parent691597cb26f236ac7471f1adf925a134c86799d6 (diff)
cfg80211/nl80211: add IBSS API
This adds IBSS API along with (preliminary) wext handlers. The wext handlers can only do IBSS so you need to call them from your own wext handlers if the mode is IBSS. The nl80211 API requires * an SSID * a channel (frequency) for the case that a new IBSS has to be created It optionally supports * a flag to fix the channel * a fixed BSSID The cfg80211 code also takes care to leave the IBSS before the netdev is set down. If wireless extensions are used, it also caches values when the interface is down and instructs the driver to join when the interface is set up. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/Makefile2
-rw-r--r--net/wireless/core.c16
-rw-r--r--net/wireless/core.h8
-rw-r--r--net/wireless/ibss.c360
-rw-r--r--net/wireless/nl80211.c182
-rw-r--r--net/wireless/nl80211.h4
-rw-r--r--net/wireless/wext-compat.c30
7 files changed, 589 insertions, 13 deletions
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 6d1e7b27b752..14ea01c4a103 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
5obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o 5obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
6obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o 6obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
7 7
8cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o 8cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o
9cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o 9cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
10 10
11ccflags-y += -D__CHECK_ENDIAN__ 11ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index d1f556535f6d..de1ac51ae4e5 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -450,6 +450,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
450 dev->ieee80211_ptr->netdev = dev; 450 dev->ieee80211_ptr->netdev = dev;
451 mutex_unlock(&rdev->devlist_mtx); 451 mutex_unlock(&rdev->devlist_mtx);
452 break; 452 break;
453 case NETDEV_GOING_DOWN:
454 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
455 break;
456 if (!dev->ieee80211_ptr->ssid_len)
457 break;
458 cfg80211_leave_ibss(rdev, dev);
459 break;
460 case NETDEV_UP:
461#ifdef CONFIG_WIRELESS_EXT
462 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
463 break;
464 if (!dev->ieee80211_ptr->wext.ssid_len)
465 break;
466 cfg80211_join_ibss(rdev, dev, &dev->ieee80211_ptr->wext);
467 break;
468#endif
453 case NETDEV_UNREGISTER: 469 case NETDEV_UNREGISTER:
454 mutex_lock(&rdev->devlist_mtx); 470 mutex_lock(&rdev->devlist_mtx);
455 if (!list_empty(&dev->ieee80211_ptr->list)) { 471 if (!list_empty(&dev->ieee80211_ptr->list)) {
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 02668b02e331..2ef3595fd6e0 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -144,4 +144,12 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev);
144void cfg80211_bss_age(struct cfg80211_registered_device *dev, 144void cfg80211_bss_age(struct cfg80211_registered_device *dev,
145 unsigned long age_secs); 145 unsigned long age_secs);
146 146
147/* IBSS */
148int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
149 struct net_device *dev,
150 struct cfg80211_ibss_params *params);
151void cfg80211_clear_ibss(struct net_device *dev);
152int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
153 struct net_device *dev);
154
147#endif /* __NET_WIRELESS_CORE_H */ 155#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
new file mode 100644
index 000000000000..2bf42fdef3a1
--- /dev/null
+++ b/net/wireless/ibss.c
@@ -0,0 +1,360 @@
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 <net/wireless.h>
11#include "nl80211.h"
12
13
14void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
15{
16 struct wireless_dev *wdev = dev->ieee80211_ptr;
17 struct cfg80211_bss *bss;
18#ifdef CONFIG_WIRELESS_EXT
19 union iwreq_data wrqu;
20#endif
21
22 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
23 return;
24
25 if (WARN_ON(!wdev->ssid_len))
26 return;
27
28 if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0)
29 return;
30
31 bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
32 wdev->ssid, wdev->ssid_len,
33 WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
34
35 if (WARN_ON(!bss))
36 return;
37
38 if (wdev->current_bss) {
39 cfg80211_unhold_bss(wdev->current_bss);
40 cfg80211_put_bss(wdev->current_bss);
41 }
42
43 cfg80211_hold_bss(bss);
44 wdev->current_bss = bss;
45 memcpy(wdev->bssid, bssid, ETH_ALEN);
46
47 nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp);
48#ifdef CONFIG_WIRELESS_EXT
49 memset(&wrqu, 0, sizeof(wrqu));
50 memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
51 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
52#endif
53}
54EXPORT_SYMBOL(cfg80211_ibss_joined);
55
56int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
57 struct net_device *dev,
58 struct cfg80211_ibss_params *params)
59{
60 struct wireless_dev *wdev = dev->ieee80211_ptr;
61 int err;
62
63 if (wdev->ssid_len)
64 return -EALREADY;
65
66#ifdef CONFIG_WIRELESS_EXT
67 wdev->wext.channel = params->channel;
68#endif
69 err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
70
71 if (err)
72 return err;
73
74 memcpy(wdev->ssid, params->ssid, params->ssid_len);
75 wdev->ssid_len = params->ssid_len;
76
77 return 0;
78}
79
80void cfg80211_clear_ibss(struct net_device *dev)
81{
82 struct wireless_dev *wdev = dev->ieee80211_ptr;
83
84 if (wdev->current_bss) {
85 cfg80211_unhold_bss(wdev->current_bss);
86 cfg80211_put_bss(wdev->current_bss);
87 }
88
89 wdev->current_bss = NULL;
90 wdev->ssid_len = 0;
91 memset(wdev->bssid, 0, ETH_ALEN);
92}
93
94int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
95 struct net_device *dev)
96{
97 int err;
98
99 err = rdev->ops->leave_ibss(&rdev->wiphy, dev);
100
101 if (err)
102 return err;
103
104 cfg80211_clear_ibss(dev);
105
106 return 0;
107}
108
109#ifdef CONFIG_WIRELESS_EXT
110static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
111 struct wireless_dev *wdev)
112{
113 enum ieee80211_band band;
114 int i;
115
116 /* try to find an IBSS channel if none requested ... */
117 if (!wdev->wext.channel) {
118 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
119 struct ieee80211_supported_band *sband;
120 struct ieee80211_channel *chan;
121
122 sband = rdev->wiphy.bands[band];
123 if (!sband)
124 continue;
125
126 for (i = 0; i < sband->n_channels; i++) {
127 chan = &sband->channels[i];
128 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
129 continue;
130 if (chan->flags & IEEE80211_CHAN_DISABLED)
131 continue;
132 wdev->wext.channel = chan;
133 break;
134 }
135
136 if (wdev->wext.channel)
137 break;
138 }
139
140 if (!wdev->wext.channel)
141 return -EINVAL;
142 }
143
144 /* don't join -- SSID is not there */
145 if (!wdev->wext.ssid_len)
146 return 0;
147
148 if (!netif_running(wdev->netdev))
149 return 0;
150
151 return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy),
152 wdev->netdev, &wdev->wext);
153}
154
155int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
156 struct iw_request_info *info,
157 struct iw_freq *freq, char *extra)
158{
159 struct wireless_dev *wdev = dev->ieee80211_ptr;
160 struct ieee80211_channel *chan;
161 int err;
162
163 /* call only for ibss! */
164 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
165 return -EINVAL;
166
167 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
168 return -EOPNOTSUPP;
169
170 chan = cfg80211_wext_freq(wdev->wiphy, freq);
171 if (chan && IS_ERR(chan))
172 return PTR_ERR(chan);
173
174 if (chan &&
175 (chan->flags & IEEE80211_CHAN_NO_IBSS ||
176 chan->flags & IEEE80211_CHAN_DISABLED))
177 return -EINVAL;
178
179 if (wdev->wext.channel == chan)
180 return 0;
181
182 if (wdev->ssid_len) {
183 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), dev);
184 if (err)
185 return err;
186 }
187
188 if (chan) {
189 wdev->wext.channel = chan;
190 wdev->wext.channel_fixed = true;
191 } else {
192 /* cfg80211_ibss_wext_join will pick one if needed */
193 wdev->wext.channel_fixed = false;
194 }
195
196 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
197}
198/* temporary symbol - mark GPL - in the future the handler won't be */
199EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq);
200
201int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
202 struct iw_request_info *info,
203 struct iw_freq *freq, char *extra)
204{
205 struct wireless_dev *wdev = dev->ieee80211_ptr;
206 struct ieee80211_channel *chan = NULL;
207
208 /* call only for ibss! */
209 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
210 return -EINVAL;
211
212 if (wdev->current_bss)
213 chan = wdev->current_bss->channel;
214 else if (wdev->wext.channel)
215 chan = wdev->wext.channel;
216
217 if (chan) {
218 freq->m = chan->center_freq;
219 freq->e = 6;
220 return 0;
221 }
222
223 /* no channel if not joining */
224 return -EINVAL;
225}
226/* temporary symbol - mark GPL - in the future the handler won't be */
227EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq);
228
229int cfg80211_ibss_wext_siwessid(struct net_device *dev,
230 struct iw_request_info *info,
231 struct iw_point *data, char *ssid)
232{
233 struct wireless_dev *wdev = dev->ieee80211_ptr;
234 size_t len = data->length;
235 int err;
236
237 /* call only for ibss! */
238 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
239 return -EINVAL;
240
241 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
242 return -EOPNOTSUPP;
243
244 if (wdev->ssid_len) {
245 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), dev);
246 if (err)
247 return err;
248 }
249
250 /* iwconfig uses nul termination in SSID.. */
251 if (len > 0 && ssid[len - 1] == '\0')
252 len--;
253
254 wdev->wext.ssid = wdev->ssid;
255 memcpy(wdev->wext.ssid, ssid, len);
256 wdev->wext.ssid_len = len;
257
258 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
259}
260/* temporary symbol - mark GPL - in the future the handler won't be */
261EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);
262
263int cfg80211_ibss_wext_giwessid(struct net_device *dev,
264 struct iw_request_info *info,
265 struct iw_point *data, char *ssid)
266{
267 struct wireless_dev *wdev = dev->ieee80211_ptr;
268
269 /* call only for ibss! */
270 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
271 return -EINVAL;
272
273 data->flags = 0;
274
275 if (wdev->ssid_len) {
276 data->flags = 1;
277 data->length = wdev->ssid_len;
278 memcpy(ssid, wdev->ssid, data->length);
279 } else if (wdev->wext.ssid) {
280 data->flags = 1;
281 data->length = wdev->wext.ssid_len;
282 memcpy(ssid, wdev->wext.ssid, data->length);
283 }
284
285 return 0;
286}
287/* temporary symbol - mark GPL - in the future the handler won't be */
288EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);
289
290int cfg80211_ibss_wext_siwap(struct net_device *dev,
291 struct iw_request_info *info,
292 struct sockaddr *ap_addr, char *extra)
293{
294 struct wireless_dev *wdev = dev->ieee80211_ptr;
295 u8 *bssid = ap_addr->sa_data;
296 int err;
297
298 /* call only for ibss! */
299 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
300 return -EINVAL;
301
302 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
303 return -EOPNOTSUPP;
304
305 if (ap_addr->sa_family != ARPHRD_ETHER)
306 return -EINVAL;
307
308 /* automatic mode */
309 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
310 bssid = NULL;
311
312 /* both automatic */
313 if (!bssid && !wdev->wext.bssid)
314 return 0;
315
316 /* fixed already - and no change */
317 if (wdev->wext.bssid && bssid &&
318 compare_ether_addr(bssid, wdev->wext.bssid) == 0)
319 return 0;
320
321 if (wdev->ssid_len) {
322 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), dev);
323 if (err)
324 return err;
325 }
326
327 if (bssid) {
328 memcpy(wdev->wext_bssid, bssid, ETH_ALEN);
329 wdev->wext.bssid = wdev->wext_bssid;
330 } else
331 wdev->wext.bssid = NULL;
332
333 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
334}
335/* temporary symbol - mark GPL - in the future the handler won't be */
336EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap);
337
338int cfg80211_ibss_wext_giwap(struct net_device *dev,
339 struct iw_request_info *info,
340 struct sockaddr *ap_addr, char *extra)
341{
342 struct wireless_dev *wdev = dev->ieee80211_ptr;
343
344 /* call only for ibss! */
345 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
346 return -EINVAL;
347
348 ap_addr->sa_family = ARPHRD_ETHER;
349
350 if (wdev->wext.bssid) {
351 memcpy(ap_addr->sa_data, wdev->wext.bssid, ETH_ALEN);
352 return 0;
353 }
354
355 memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN);
356 return 0;
357}
358/* temporary symbol - mark GPL - in the future the handler won't be */
359EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap);
360#endif
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d2cfde659e76..16f86356ac97 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -116,6 +116,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
116 .len = IEEE80211_MAX_SSID_LEN }, 116 .len = IEEE80211_MAX_SSID_LEN },
117 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, 117 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
118 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, 118 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
119 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
119}; 120};
120 121
121/* IE validation */ 122/* IE validation */
@@ -322,6 +323,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
322 CMD(assoc, ASSOCIATE); 323 CMD(assoc, ASSOCIATE);
323 CMD(deauth, DEAUTHENTICATE); 324 CMD(deauth, DEAUTHENTICATE);
324 CMD(disassoc, DISASSOCIATE); 325 CMD(disassoc, DISASSOCIATE);
326 CMD(join_ibss, JOIN_IBSS);
325 327
326#undef CMD 328#undef CMD
327 nla_nest_end(msg, nl_cmds); 329 nla_nest_end(msg, nl_cmds);
@@ -668,7 +670,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
668 struct cfg80211_registered_device *drv; 670 struct cfg80211_registered_device *drv;
669 struct vif_params params; 671 struct vif_params params;
670 int err, ifindex; 672 int err, ifindex;
671 enum nl80211_iftype type; 673 enum nl80211_iftype otype, ntype;
672 struct net_device *dev; 674 struct net_device *dev;
673 u32 _flags, *flags = NULL; 675 u32 _flags, *flags = NULL;
674 bool change = false; 676 bool change = false;
@@ -682,30 +684,27 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
682 goto unlock_rtnl; 684 goto unlock_rtnl;
683 685
684 ifindex = dev->ifindex; 686 ifindex = dev->ifindex;
685 type = dev->ieee80211_ptr->iftype; 687 otype = ntype = dev->ieee80211_ptr->iftype;
686 dev_put(dev); 688 dev_put(dev);
687 689
688 if (info->attrs[NL80211_ATTR_IFTYPE]) { 690 if (info->attrs[NL80211_ATTR_IFTYPE]) {
689 enum nl80211_iftype ntype;
690
691 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); 691 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
692 if (type != ntype) 692 if (otype != ntype)
693 change = true; 693 change = true;
694 type = ntype; 694 if (ntype > NL80211_IFTYPE_MAX) {
695 if (type > NL80211_IFTYPE_MAX) {
696 err = -EINVAL; 695 err = -EINVAL;
697 goto unlock; 696 goto unlock;
698 } 697 }
699 } 698 }
700 699
701 if (!drv->ops->change_virtual_intf || 700 if (!drv->ops->change_virtual_intf ||
702 !(drv->wiphy.interface_modes & (1 << type))) { 701 !(drv->wiphy.interface_modes & (1 << ntype))) {
703 err = -EOPNOTSUPP; 702 err = -EOPNOTSUPP;
704 goto unlock; 703 goto unlock;
705 } 704 }
706 705
707 if (info->attrs[NL80211_ATTR_MESH_ID]) { 706 if (info->attrs[NL80211_ATTR_MESH_ID]) {
708 if (type != NL80211_IFTYPE_MESH_POINT) { 707 if (ntype != NL80211_IFTYPE_MESH_POINT) {
709 err = -EINVAL; 708 err = -EINVAL;
710 goto unlock; 709 goto unlock;
711 } 710 }
@@ -715,7 +714,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
715 } 714 }
716 715
717 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { 716 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
718 if (type != NL80211_IFTYPE_MONITOR) { 717 if (ntype != NL80211_IFTYPE_MONITOR) {
719 err = -EINVAL; 718 err = -EINVAL;
720 goto unlock; 719 goto unlock;
721 } 720 }
@@ -730,12 +729,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
730 729
731 if (change) 730 if (change)
732 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, 731 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
733 type, flags, &params); 732 ntype, flags, &params);
734 else 733 else
735 err = 0; 734 err = 0;
736 735
737 dev = __dev_get_by_index(&init_net, ifindex); 736 dev = __dev_get_by_index(&init_net, ifindex);
738 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); 737 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));
738
739 if (dev && !err && (ntype != otype)) {
740 if (otype == NL80211_IFTYPE_ADHOC)
741 cfg80211_clear_ibss(dev);
742 }
739 743
740 unlock: 744 unlock:
741 cfg80211_put_dev(drv); 745 cfg80211_put_dev(drv);
@@ -3052,6 +3056,114 @@ unlock_rtnl:
3052 return err; 3056 return err;
3053} 3057}
3054 3058
3059static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3060{
3061 struct cfg80211_registered_device *drv;
3062 struct net_device *dev;
3063 struct cfg80211_ibss_params ibss;
3064 struct wiphy *wiphy;
3065 int err;
3066
3067 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3068 return -EINVAL;
3069
3070 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3071 !info->attrs[NL80211_ATTR_SSID] ||
3072 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3073 return -EINVAL;
3074
3075 rtnl_lock();
3076
3077 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3078 if (err)
3079 goto unlock_rtnl;
3080
3081 if (!drv->ops->join_ibss) {
3082 err = -EOPNOTSUPP;
3083 goto out;
3084 }
3085
3086 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3087 err = -EOPNOTSUPP;
3088 goto out;
3089 }
3090
3091 if (!netif_running(dev)) {
3092 err = -ENETDOWN;
3093 goto out;
3094 }
3095
3096 wiphy = &drv->wiphy;
3097 memset(&ibss, 0, sizeof(ibss));
3098
3099 if (info->attrs[NL80211_ATTR_MAC])
3100 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3101 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3102 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3103
3104 if (info->attrs[NL80211_ATTR_IE]) {
3105 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3106 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3107 }
3108
3109 ibss.channel = ieee80211_get_channel(wiphy,
3110 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3111 if (!ibss.channel ||
3112 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3113 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3114 err = -EINVAL;
3115 goto out;
3116 }
3117
3118 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
3119
3120 err = cfg80211_join_ibss(drv, dev, &ibss);
3121
3122out:
3123 cfg80211_put_dev(drv);
3124 dev_put(dev);
3125unlock_rtnl:
3126 rtnl_unlock();
3127 return err;
3128}
3129
3130static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3131{
3132 struct cfg80211_registered_device *drv;
3133 struct net_device *dev;
3134 int err;
3135
3136 rtnl_lock();
3137
3138 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3139 if (err)
3140 goto unlock_rtnl;
3141
3142 if (!drv->ops->leave_ibss) {
3143 err = -EOPNOTSUPP;
3144 goto out;
3145 }
3146
3147 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3148 err = -EOPNOTSUPP;
3149 goto out;
3150 }
3151
3152 if (!netif_running(dev)) {
3153 err = -ENETDOWN;
3154 goto out;
3155 }
3156
3157 err = cfg80211_leave_ibss(drv, dev);
3158
3159out:
3160 cfg80211_put_dev(drv);
3161 dev_put(dev);
3162unlock_rtnl:
3163 rtnl_unlock();
3164 return err;
3165}
3166
3055static struct genl_ops nl80211_ops[] = { 3167static struct genl_ops nl80211_ops[] = {
3056 { 3168 {
3057 .cmd = NL80211_CMD_GET_WIPHY, 3169 .cmd = NL80211_CMD_GET_WIPHY,
@@ -3253,6 +3365,18 @@ static struct genl_ops nl80211_ops[] = {
3253 .policy = nl80211_policy, 3365 .policy = nl80211_policy,
3254 .flags = GENL_ADMIN_PERM, 3366 .flags = GENL_ADMIN_PERM,
3255 }, 3367 },
3368 {
3369 .cmd = NL80211_CMD_JOIN_IBSS,
3370 .doit = nl80211_join_ibss,
3371 .policy = nl80211_policy,
3372 .flags = GENL_ADMIN_PERM,
3373 },
3374 {
3375 .cmd = NL80211_CMD_LEAVE_IBSS,
3376 .doit = nl80211_leave_ibss,
3377 .policy = nl80211_policy,
3378 .flags = GENL_ADMIN_PERM,
3379 },
3256}; 3380};
3257static struct genl_multicast_group nl80211_mlme_mcgrp = { 3381static struct genl_multicast_group nl80211_mlme_mcgrp = {
3258 .name = "mlme", 3382 .name = "mlme",
@@ -3466,6 +3590,40 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
3466 NL80211_CMD_DISASSOCIATE); 3590 NL80211_CMD_DISASSOCIATE);
3467} 3591}
3468 3592
3593void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
3594 struct net_device *netdev, const u8 *bssid,
3595 gfp_t gfp)
3596{
3597 struct sk_buff *msg;
3598 void *hdr;
3599
3600 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
3601 if (!msg)
3602 return;
3603
3604 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
3605 if (!hdr) {
3606 nlmsg_free(msg);
3607 return;
3608 }
3609
3610 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3611 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3612 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
3613
3614 if (genlmsg_end(msg, hdr) < 0) {
3615 nlmsg_free(msg);
3616 return;
3617 }
3618
3619 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
3620 return;
3621
3622 nla_put_failure:
3623 genlmsg_cancel(msg, hdr);
3624 nlmsg_free(msg);
3625}
3626
3469void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, 3627void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
3470 struct net_device *netdev, const u8 *addr, 3628 struct net_device *netdev, const u8 *addr,
3471 enum nl80211_key_type key_type, int key_id, 3629 enum nl80211_key_type key_type, int key_id,
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index b3aaa59afa08..17d2d8bfaf75 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -34,4 +34,8 @@ nl80211_send_beacon_hint_event(struct wiphy *wiphy,
34 struct ieee80211_channel *channel_before, 34 struct ieee80211_channel *channel_before,
35 struct ieee80211_channel *channel_after); 35 struct ieee80211_channel *channel_after);
36 36
37void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
38 struct net_device *netdev, const u8 *bssid,
39 gfp_t gfp);
40
37#endif /* __NET_WIRELESS_NL80211_H */ 41#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 6fd7bf7b4481..57eaea26b67a 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -285,3 +285,33 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
285 } 285 }
286} 286}
287EXPORT_SYMBOL(cfg80211_wext_siwmlme); 287EXPORT_SYMBOL(cfg80211_wext_siwmlme);
288
289
290/**
291 * cfg80211_wext_freq - get wext frequency for non-"auto"
292 * @wiphy: the wiphy
293 * @freq: the wext freq encoding
294 *
295 * Returns a channel, %NULL for auto, or an ERR_PTR for errors!
296 */
297struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy,
298 struct iw_freq *freq)
299{
300 if (freq->e == 0) {
301 if (freq->m < 0)
302 return NULL;
303 else
304 return ieee80211_get_channel(wiphy,
305 ieee80211_channel_to_frequency(freq->m));
306 } else {
307 int i, div = 1000000;
308 for (i = 0; i < freq->e; i++)
309 div /= 10;
310 if (div > 0)
311 return ieee80211_get_channel(wiphy, freq->m / div);
312 else
313 return ERR_PTR(-EINVAL);
314 }
315
316}
317EXPORT_SYMBOL(cfg80211_wext_freq);