cfg80211: fix locking
[safe/jmp/linux-2.6] / net / wireless / wext-sme.c
1 /*
2  * cfg80211 wext compat for managed mode.
3  *
4  * Copyright 2009       Johannes Berg <johannes@sipsolutions.net>
5  * Copyright (C) 2009   Intel Corporation. All rights reserved.
6  */
7
8 #include <linux/etherdevice.h>
9 #include <linux/if_arp.h>
10 #include <net/cfg80211.h>
11 #include "nl80211.h"
12
13 static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
14                                      struct wireless_dev *wdev)
15 {
16         int err;
17
18         ASSERT_RDEV_LOCK(rdev);
19         ASSERT_WDEV_LOCK(wdev);
20
21         if (!netif_running(wdev->netdev))
22                 return 0;
23
24         wdev->wext.connect.ie = wdev->wext.ie;
25         wdev->wext.connect.ie_len = wdev->wext.ie_len;
26         wdev->wext.connect.privacy = wdev->wext.default_key != -1;
27
28         err = 0;
29         if (wdev->wext.connect.ssid_len != 0)
30                 err = __cfg80211_connect(rdev, wdev->netdev,
31                                          &wdev->wext.connect);
32
33         return err;
34 }
35
36 int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
37                               struct iw_request_info *info,
38                               struct iw_freq *freq, char *extra)
39 {
40         struct wireless_dev *wdev = dev->ieee80211_ptr;
41         struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
42         struct ieee80211_channel *chan;
43         int err;
44
45         /* call only for station! */
46         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
47                 return -EINVAL;
48
49         chan = cfg80211_wext_freq(wdev->wiphy, freq);
50         if (chan && IS_ERR(chan))
51                 return PTR_ERR(chan);
52
53         if (chan && (chan->flags & IEEE80211_CHAN_DISABLED))
54                 return -EINVAL;
55
56         cfg80211_lock_rdev(rdev);
57         wdev_lock(wdev);
58
59         if (wdev->wext.connect.channel == chan) {
60                 err = 0;
61                 goto out;
62         }
63
64         if (wdev->sme_state != CFG80211_SME_IDLE) {
65                 bool event = true;
66                 /* if SSID set, we'll try right again, avoid event */
67                 if (wdev->wext.connect.ssid_len)
68                         event = false;
69                 err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
70                                             dev, WLAN_REASON_DEAUTH_LEAVING,
71                                             event);
72                 if (err)
73                         goto out;
74         }
75
76
77         wdev->wext.connect.channel = chan;
78
79         /* SSID is not set, we just want to switch channel */
80         if (wdev->wext.connect.ssid_len && chan) {
81                 err = -EOPNOTSUPP;
82                 if (rdev->ops->set_channel)
83                         err = rdev->ops->set_channel(wdev->wiphy, chan,
84                                                      NL80211_CHAN_NO_HT);
85                 goto out;
86         }
87
88         err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
89  out:
90         wdev_unlock(wdev);
91         cfg80211_unlock_rdev(rdev);
92         return err;
93 }
94 /* temporary symbol - mark GPL - in the future the handler won't be */
95 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq);
96
97 int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
98                               struct iw_request_info *info,
99                               struct iw_freq *freq, char *extra)
100 {
101         struct wireless_dev *wdev = dev->ieee80211_ptr;
102         struct ieee80211_channel *chan = NULL;
103
104         /* call only for station! */
105         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
106                 return -EINVAL;
107
108         wdev_lock(wdev);
109         if (wdev->current_bss)
110                 chan = wdev->current_bss->pub.channel;
111         else if (wdev->wext.connect.channel)
112                 chan = wdev->wext.connect.channel;
113         wdev_unlock(wdev);
114
115         if (chan) {
116                 freq->m = chan->center_freq;
117                 freq->e = 6;
118                 return 0;
119         }
120
121         /* no channel if not joining */
122         return -EINVAL;
123 }
124 /* temporary symbol - mark GPL - in the future the handler won't be */
125 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq);
126
127 int cfg80211_mgd_wext_siwessid(struct net_device *dev,
128                                struct iw_request_info *info,
129                                struct iw_point *data, char *ssid)
130 {
131         struct wireless_dev *wdev = dev->ieee80211_ptr;
132         size_t len = data->length;
133         int err;
134
135         /* call only for station! */
136         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
137                 return -EINVAL;
138
139         if (!data->flags)
140                 len = 0;
141
142         /* iwconfig uses nul termination in SSID.. */
143         if (len > 0 && ssid[len - 1] == '\0')
144                 len--;
145
146         cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy));
147         wdev_lock(wdev);
148
149         err = 0;
150
151         if (wdev->wext.connect.ssid && len &&
152             len == wdev->wext.connect.ssid_len &&
153             memcmp(wdev->wext.connect.ssid, ssid, len))
154                 goto out;
155
156         if (wdev->sme_state != CFG80211_SME_IDLE) {
157                 bool event = true;
158                 /* if SSID set now, we'll try to connect, avoid event */
159                 if (len)
160                         event = false;
161                 err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
162                                             dev, WLAN_REASON_DEAUTH_LEAVING,
163                                             event);
164                 if (err)
165                         goto out;
166         }
167
168         wdev->wext.connect.ssid = wdev->wext.ssid;
169         memcpy(wdev->wext.ssid, ssid, len);
170         wdev->wext.connect.ssid_len = len;
171
172         wdev->wext.connect.crypto.control_port = false;
173
174         err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
175  out:
176         wdev_unlock(wdev);
177         cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy));
178         return err;
179 }
180 /* temporary symbol - mark GPL - in the future the handler won't be */
181 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid);
182
183 int cfg80211_mgd_wext_giwessid(struct net_device *dev,
184                                struct iw_request_info *info,
185                                struct iw_point *data, char *ssid)
186 {
187         struct wireless_dev *wdev = dev->ieee80211_ptr;
188
189         /* call only for station! */
190         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
191                 return -EINVAL;
192
193         data->flags = 0;
194
195         wdev_lock(wdev);
196         if (wdev->ssid_len) {
197                 data->flags = 1;
198                 data->length = wdev->ssid_len;
199                 memcpy(ssid, wdev->ssid, data->length);
200         } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
201                 data->flags = 1;
202                 data->length = wdev->wext.connect.ssid_len;
203                 memcpy(ssid, wdev->wext.connect.ssid, data->length);
204         } else
205                 data->flags = 0;
206         wdev_unlock(wdev);
207
208         return 0;
209 }
210 /* temporary symbol - mark GPL - in the future the handler won't be */
211 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid);
212
213 int cfg80211_mgd_wext_siwap(struct net_device *dev,
214                             struct iw_request_info *info,
215                             struct sockaddr *ap_addr, char *extra)
216 {
217         struct wireless_dev *wdev = dev->ieee80211_ptr;
218         u8 *bssid = ap_addr->sa_data;
219         int err;
220
221         /* call only for station! */
222         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
223                 return -EINVAL;
224
225         if (ap_addr->sa_family != ARPHRD_ETHER)
226                 return -EINVAL;
227
228         /* automatic mode */
229         if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
230                 bssid = NULL;
231
232         cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy));
233         wdev_lock(wdev);
234
235         err = 0;
236         /* both automatic */
237         if (!bssid && !wdev->wext.connect.bssid)
238                 goto out;
239
240         /* fixed already - and no change */
241         if (wdev->wext.connect.bssid && bssid &&
242             compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0)
243                 goto out;
244
245         if (wdev->sme_state != CFG80211_SME_IDLE) {
246                 err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
247                                             dev, WLAN_REASON_DEAUTH_LEAVING,
248                                             false);
249                 if (err)
250                         goto out;
251         }
252
253         if (bssid) {
254                 memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
255                 wdev->wext.connect.bssid = wdev->wext.bssid;
256         } else
257                 wdev->wext.connect.bssid = NULL;
258
259         err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
260  out:
261         wdev_unlock(wdev);
262         cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy));
263         return err;
264 }
265 /* temporary symbol - mark GPL - in the future the handler won't be */
266 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap);
267
268 int cfg80211_mgd_wext_giwap(struct net_device *dev,
269                             struct iw_request_info *info,
270                             struct sockaddr *ap_addr, char *extra)
271 {
272         struct wireless_dev *wdev = dev->ieee80211_ptr;
273
274         /* call only for station! */
275         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
276                 return -EINVAL;
277
278         ap_addr->sa_family = ARPHRD_ETHER;
279
280         wdev_lock(wdev);
281         if (wdev->current_bss)
282                 memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
283         else if (wdev->wext.connect.bssid)
284                 memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN);
285         else
286                 memset(ap_addr->sa_data, 0, ETH_ALEN);
287         wdev_unlock(wdev);
288
289         return 0;
290 }
291 /* temporary symbol - mark GPL - in the future the handler won't be */
292 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap);
293
294 int cfg80211_wext_siwgenie(struct net_device *dev,
295                            struct iw_request_info *info,
296                            struct iw_point *data, char *extra)
297 {
298         struct wireless_dev *wdev = dev->ieee80211_ptr;
299         struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
300         u8 *ie = extra;
301         int ie_len = data->length, err;
302
303         if (wdev->iftype != NL80211_IFTYPE_STATION)
304                 return -EOPNOTSUPP;
305
306         if (!ie_len)
307                 ie = NULL;
308
309         wdev_lock(wdev);
310
311         /* no change */
312         err = 0;
313         if (wdev->wext.ie_len == ie_len &&
314             memcmp(wdev->wext.ie, ie, ie_len) == 0)
315                 goto out;
316
317         if (ie_len) {
318                 ie = kmemdup(extra, ie_len, GFP_KERNEL);
319                 if (!ie) {
320                         err = -ENOMEM;
321                         goto out;
322                 }
323         } else
324                 ie = NULL;
325
326         kfree(wdev->wext.ie);
327         wdev->wext.ie = ie;
328         wdev->wext.ie_len = ie_len;
329
330         if (wdev->sme_state != CFG80211_SME_IDLE) {
331                 err = __cfg80211_disconnect(rdev, dev,
332                                             WLAN_REASON_DEAUTH_LEAVING, false);
333                 if (err)
334                         goto out;
335         }
336
337         /* userspace better not think we'll reconnect */
338         err = 0;
339  out:
340         wdev_unlock(wdev);
341         return err;
342 }
343 EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie);
344
345 int cfg80211_wext_siwmlme(struct net_device *dev,
346                           struct iw_request_info *info,
347                           struct iw_point *data, char *extra)
348 {
349         struct wireless_dev *wdev = dev->ieee80211_ptr;
350         struct iw_mlme *mlme = (struct iw_mlme *)extra;
351         struct cfg80211_registered_device *rdev;
352         int err;
353
354         if (!wdev)
355                 return -EOPNOTSUPP;
356
357         rdev = wiphy_to_dev(wdev->wiphy);
358
359         if (wdev->iftype != NL80211_IFTYPE_STATION)
360                 return -EINVAL;
361
362         if (mlme->addr.sa_family != ARPHRD_ETHER)
363                 return -EINVAL;
364
365         wdev_lock(wdev);
366         switch (mlme->cmd) {
367         case IW_MLME_DEAUTH:
368         case IW_MLME_DISASSOC:
369                 err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
370                                             true);
371                 break;
372         default:
373                 err = -EOPNOTSUPP;
374                 break;
375         }
376         wdev_unlock(wdev);
377
378         return err;
379 }
380 EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme);