diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-04-19 15:24:32 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-04-22 16:57:17 -0400 |
commit | 04a773ade0680d862b479d7219973df60f7a3834 (patch) | |
tree | fc759eb79099fefd7f1329bcb2b703008cb0adfe /net/wireless | |
parent | 691597cb26f236ac7471f1adf925a134c86799d6 (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/Makefile | 2 | ||||
-rw-r--r-- | net/wireless/core.c | 16 | ||||
-rw-r--r-- | net/wireless/core.h | 8 | ||||
-rw-r--r-- | net/wireless/ibss.c | 360 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 182 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 4 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 30 |
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 | |||
5 | obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o | 5 | obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o |
6 | obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o | 6 | obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o |
7 | 7 | ||
8 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o | 8 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o |
9 | cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o | 9 | cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o |
10 | 10 | ||
11 | ccflags-y += -D__CHECK_ENDIAN__ | 11 | ccflags-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); | |||
144 | void cfg80211_bss_age(struct cfg80211_registered_device *dev, | 144 | void cfg80211_bss_age(struct cfg80211_registered_device *dev, |
145 | unsigned long age_secs); | 145 | unsigned long age_secs); |
146 | 146 | ||
147 | /* IBSS */ | ||
148 | int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, | ||
149 | struct net_device *dev, | ||
150 | struct cfg80211_ibss_params *params); | ||
151 | void cfg80211_clear_ibss(struct net_device *dev); | ||
152 | int 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 | |||
14 | void 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 | } | ||
54 | EXPORT_SYMBOL(cfg80211_ibss_joined); | ||
55 | |||
56 | int 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 | |||
80 | void 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 | |||
94 | int 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 | ||
110 | static 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 | |||
155 | int 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 */ | ||
199 | EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); | ||
200 | |||
201 | int 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 */ | ||
227 | EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq); | ||
228 | |||
229 | int 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 */ | ||
261 | EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); | ||
262 | |||
263 | int 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 */ | ||
288 | EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid); | ||
289 | |||
290 | int 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 */ | ||
336 | EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); | ||
337 | |||
338 | int 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 */ | ||
359 | EXPORT_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, ¶ms); | 732 | ntype, flags, ¶ms); |
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 | ||
3059 | static 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 | |||
3122 | out: | ||
3123 | cfg80211_put_dev(drv); | ||
3124 | dev_put(dev); | ||
3125 | unlock_rtnl: | ||
3126 | rtnl_unlock(); | ||
3127 | return err; | ||
3128 | } | ||
3129 | |||
3130 | static 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 | |||
3159 | out: | ||
3160 | cfg80211_put_dev(drv); | ||
3161 | dev_put(dev); | ||
3162 | unlock_rtnl: | ||
3163 | rtnl_unlock(); | ||
3164 | return err; | ||
3165 | } | ||
3166 | |||
3055 | static struct genl_ops nl80211_ops[] = { | 3167 | static 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 | }; |
3257 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 3381 | static 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 | ||
3593 | void 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 | |||
3469 | void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, | 3627 | void 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 | ||
37 | void 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 | } |
287 | EXPORT_SYMBOL(cfg80211_wext_siwmlme); | 287 | EXPORT_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 | */ | ||
297 | struct 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 | } | ||
317 | EXPORT_SYMBOL(cfg80211_wext_freq); | ||