diff options
Diffstat (limited to 'net')
-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); | ||