[PATCH] SoftMAC: Prevent multiple authentication attempts on the same network
[linux-2.6] / net / ieee80211 / softmac / ieee80211softmac_wx.c
1 /*
2  * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
3  *
4  * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
5  *                          Joseph Jezak <josejx@gentoo.org>
6  *                          Larry Finger <Larry.Finger@lwfinger.net>
7  *                          Danny van Dyk <kugelfang@gentoo.org>
8  *                          Michael Buesch <mbuesch@freenet.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of version 2 of the GNU General Public License as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
22  *
23  * The full GNU General Public License is included in this distribution in the
24  * file called COPYING.
25  */
26
27 #include "ieee80211softmac_priv.h"
28
29 #include <net/iw_handler.h>
30 /* for is_broadcast_ether_addr and is_zero_ether_addr */
31 #include <linux/etherdevice.h>
32
33 int
34 ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
35                                  struct iw_request_info *info,
36                                  union iwreq_data *data,
37                                  char *extra)
38 {
39         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
40         return ieee80211softmac_start_scan(sm);
41 }
42 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
43
44
45 /* if we're still scanning, return -EAGAIN so that userspace tools
46  * can get the complete scan results, otherwise return 0. */
47 int
48 ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
49                                      struct iw_request_info *info,
50                                      union iwreq_data *data,
51                                      char *extra)
52 {
53         unsigned long flags;
54         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
55
56         spin_lock_irqsave(&sm->lock, flags);
57         if (sm->scanning) {
58                 spin_unlock_irqrestore(&sm->lock, flags);
59                 return -EAGAIN;
60         }
61         spin_unlock_irqrestore(&sm->lock, flags);
62         return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
63 }
64 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
65
66 int
67 ieee80211softmac_wx_set_essid(struct net_device *net_dev,
68                               struct iw_request_info *info,
69                               union iwreq_data *data,
70                               char *extra)
71 {
72         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
73         struct ieee80211softmac_network *n;
74         struct ieee80211softmac_auth_queue_item *authptr;
75         int length = 0;
76         unsigned long flags;
77
78         /* Check if we're already associating to this or another network
79          * If it's another network, cancel and start over with our new network
80          * If it's our network, ignore the change, we're already doing it!
81          */
82         if((sm->associnfo.associating || sm->associated) &&
83            (data->essid.flags && data->essid.length && extra)) {
84                 /* Get the associating network */
85                 n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid);
86                 if(n && n->essid.len == (data->essid.length - 1) &&
87                    !memcmp(n->essid.data, extra, n->essid.len)) {
88                         dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n",
89                                 MAC_ARG(sm->associnfo.bssid));
90                         return 0;
91                 } else {
92                         dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
93                         spin_lock_irqsave(&sm->lock,flags);
94                         /* Cancel assoc work */
95                         cancel_delayed_work(&sm->associnfo.work);
96                         /* We don't have to do this, but it's a little cleaner */
97                         list_for_each_entry(authptr, &sm->auth_queue, list)
98                                 cancel_delayed_work(&authptr->work);
99                         sm->associnfo.bssvalid = 0;
100                         sm->associnfo.bssfixed = 0;
101                         spin_unlock_irqrestore(&sm->lock,flags);
102                         flush_scheduled_work();
103                 }
104         }
105
106
107         spin_lock_irqsave(&sm->lock, flags);
108
109         sm->associnfo.static_essid = 0;
110         sm->associnfo.assoc_wait = 0;
111
112         if (data->essid.flags && data->essid.length && extra /*required?*/) {
113                 length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
114                 if (length) {
115                         memcpy(sm->associnfo.req_essid.data, extra, length);
116                         sm->associnfo.static_essid = 1;
117                 }
118         }
119
120         /* set our requested ESSID length.
121          * If applicable, we have already copied the data in */
122         sm->associnfo.req_essid.len = length;
123
124         /* queue lower level code to do work (if necessary) */
125         schedule_work(&sm->associnfo.work);
126
127         spin_unlock_irqrestore(&sm->lock, flags);
128         return 0;
129 }
130 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
131
132 int
133 ieee80211softmac_wx_get_essid(struct net_device *net_dev,
134                               struct iw_request_info *info,
135                               union iwreq_data *data,
136                               char *extra)
137 {
138         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
139         unsigned long flags;
140
141         /* avoid getting inconsistent information */
142         spin_lock_irqsave(&sm->lock, flags);
143         /* If all fails, return ANY (empty) */
144         data->essid.length = 0;
145         data->essid.flags = 0;  /* active */
146         
147         /* If we have a statically configured ESSID then return it */
148         if (sm->associnfo.static_essid) {
149                 data->essid.length = sm->associnfo.req_essid.len;
150                 data->essid.flags = 1;  /* active */
151                 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
152         }
153         
154         /* If we're associating/associated, return that */
155         if (sm->associated || sm->associnfo.associating) {
156                 data->essid.length = sm->associnfo.associate_essid.len;
157                 data->essid.flags = 1;  /* active */
158                 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
159         }
160         spin_unlock_irqrestore(&sm->lock, flags);
161         return 0;
162 }
163 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
164
165 int
166 ieee80211softmac_wx_set_rate(struct net_device *net_dev,
167                              struct iw_request_info *info,
168                              union iwreq_data *data,
169                              char *extra)
170 {
171         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
172         struct ieee80211_device *ieee = mac->ieee;
173         unsigned long flags;
174         s32 in_rate = data->bitrate.value;
175         u8 rate;
176         int is_ofdm = 0;
177         int err = -EINVAL;
178
179         if (in_rate == -1) {
180                 /* FIXME: We don't correctly handle backing down to lower
181                    rates, so 801.11g devices start off at 11M for now. People
182                    can manually change it if they really need to, but 11M is
183                    more reliable. Note similar logic in
184                    ieee80211softmac_wx_set_rate() */     
185                 if (ieee->modulation & IEEE80211_CCK_MODULATION)
186                         in_rate = 11000000;
187                 else
188                         in_rate = 54000000;
189         }
190
191         switch (in_rate) {
192         case 1000000:
193                 rate = IEEE80211_CCK_RATE_1MB;
194                 break;
195         case 2000000:
196                 rate = IEEE80211_CCK_RATE_2MB;
197                 break;
198         case 5500000:
199                 rate = IEEE80211_CCK_RATE_5MB;
200                 break;
201         case 11000000:
202                 rate = IEEE80211_CCK_RATE_11MB;
203                 break;
204         case 6000000:
205                 rate = IEEE80211_OFDM_RATE_6MB;
206                 is_ofdm = 1;
207                 break;
208         case 9000000:
209                 rate = IEEE80211_OFDM_RATE_9MB;
210                 is_ofdm = 1;
211                 break;
212         case 12000000:
213                 rate = IEEE80211_OFDM_RATE_12MB;
214                 is_ofdm = 1;
215                 break;
216         case 18000000:
217                 rate = IEEE80211_OFDM_RATE_18MB;
218                 is_ofdm = 1;
219                 break;
220         case 24000000:
221                 rate = IEEE80211_OFDM_RATE_24MB;
222                 is_ofdm = 1;
223                 break;
224         case 36000000:
225                 rate = IEEE80211_OFDM_RATE_36MB;
226                 is_ofdm = 1;
227                 break;
228         case 48000000:
229                 rate = IEEE80211_OFDM_RATE_48MB;
230                 is_ofdm = 1;
231                 break;
232         case 54000000:
233                 rate = IEEE80211_OFDM_RATE_54MB;
234                 is_ofdm = 1;
235                 break;
236         default:
237                 goto out;
238         }
239
240         spin_lock_irqsave(&mac->lock, flags);
241
242         /* Check if correct modulation for this PHY. */
243         if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
244                 goto out_unlock;
245
246         mac->txrates.user_rate = rate;
247         ieee80211softmac_recalc_txrates(mac);
248         err = 0;
249
250 out_unlock:     
251         spin_unlock_irqrestore(&mac->lock, flags);
252 out:
253         return err;
254 }
255 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
256
257 int
258 ieee80211softmac_wx_get_rate(struct net_device *net_dev,
259                              struct iw_request_info *info,
260                              union iwreq_data *data,
261                              char *extra)
262 {
263         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
264         unsigned long flags;
265         int err = -EINVAL;
266
267         spin_lock_irqsave(&mac->lock, flags);
268         switch (mac->txrates.default_rate) {
269         case IEEE80211_CCK_RATE_1MB:
270                 data->bitrate.value = 1000000;
271                 break;
272         case IEEE80211_CCK_RATE_2MB:
273                 data->bitrate.value = 2000000;
274                 break;
275         case IEEE80211_CCK_RATE_5MB:
276                 data->bitrate.value = 5500000;
277                 break;
278         case IEEE80211_CCK_RATE_11MB:
279                 data->bitrate.value = 11000000;
280                 break;
281         case IEEE80211_OFDM_RATE_6MB:
282                 data->bitrate.value = 6000000;
283                 break;
284         case IEEE80211_OFDM_RATE_9MB:
285                 data->bitrate.value = 9000000;
286                 break;
287         case IEEE80211_OFDM_RATE_12MB:
288                 data->bitrate.value = 12000000;
289                 break;
290         case IEEE80211_OFDM_RATE_18MB:
291                 data->bitrate.value = 18000000;
292                 break;
293         case IEEE80211_OFDM_RATE_24MB:
294                 data->bitrate.value = 24000000;
295                 break;
296         case IEEE80211_OFDM_RATE_36MB:
297                 data->bitrate.value = 36000000;
298                 break;
299         case IEEE80211_OFDM_RATE_48MB:
300                 data->bitrate.value = 48000000;
301                 break;
302         case IEEE80211_OFDM_RATE_54MB:
303                 data->bitrate.value = 54000000;
304                 break;
305         default:
306                 assert(0);
307                 goto out_unlock;
308         }
309         err = 0;
310 out_unlock:
311         spin_unlock_irqrestore(&mac->lock, flags);
312
313         return err;
314 }
315 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
316
317 int
318 ieee80211softmac_wx_get_wap(struct net_device *net_dev,
319                             struct iw_request_info *info,
320                             union iwreq_data *data,
321                             char *extra)
322 {
323         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
324         int err = 0;
325         unsigned long flags;
326
327         spin_lock_irqsave(&mac->lock, flags);
328         if (mac->associnfo.bssvalid)
329                 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
330         else
331                 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
332         data->ap_addr.sa_family = ARPHRD_ETHER;
333         spin_unlock_irqrestore(&mac->lock, flags);
334         return err;
335 }
336 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
337
338 int
339 ieee80211softmac_wx_set_wap(struct net_device *net_dev,
340                             struct iw_request_info *info,
341                             union iwreq_data *data,
342                             char *extra)
343 {
344         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
345         unsigned long flags;
346
347         /* sanity check */
348         if (data->ap_addr.sa_family != ARPHRD_ETHER) {
349                 return -EINVAL;
350         }
351
352         spin_lock_irqsave(&mac->lock, flags);
353         if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
354                 /* the bssid we have is not to be fixed any longer,
355                  * and we should reassociate to the best AP. */
356                 mac->associnfo.bssfixed = 0;
357                 /* force reassociation */
358                 mac->associnfo.bssvalid = 0;
359                 if (mac->associated)
360                         schedule_work(&mac->associnfo.work);
361         } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
362                 /* the bssid we have is no longer fixed */
363                 mac->associnfo.bssfixed = 0;
364         } else {
365                 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
366                         if (mac->associnfo.associating || mac->associated) {
367                         /* bssid unchanged and associated or associating - just return */
368                                 goto out;
369                         }
370                 } else {
371                         /* copy new value in data->ap_addr.sa_data to bssid */
372                         memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
373                 }
374                 /* tell the other code that this bssid should be used no matter what */
375                 mac->associnfo.bssfixed = 1;
376                 /* queue associate if new bssid or (old one again and not associated) */
377                 schedule_work(&mac->associnfo.work);
378         }
379
380  out:
381         spin_unlock_irqrestore(&mac->lock, flags);
382         return 0;
383 }
384 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
385
386 int
387 ieee80211softmac_wx_set_genie(struct net_device *dev,
388                               struct iw_request_info *info,
389                               union iwreq_data *wrqu,
390                               char *extra)
391 {
392         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
393         unsigned long flags;
394         int err = 0;
395         char *buf;
396         int i;
397         
398         spin_lock_irqsave(&mac->lock, flags);
399         /* bleh. shouldn't be locked for that kmalloc... */
400
401         if (wrqu->data.length) {
402                 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
403                         /* this is an IE, so the length must be
404                          * correct. Is it possible though that
405                          * more than one IE is passed in?
406                          */
407                         err = -EINVAL;
408                         goto out;
409                 }
410                 if (mac->wpa.IEbuflen <= wrqu->data.length) {
411                         buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
412                         if (!buf) {
413                                 err = -ENOMEM;
414                                 goto out;
415                         }
416                         kfree(mac->wpa.IE);
417                         mac->wpa.IE = buf;
418                         mac->wpa.IEbuflen = wrqu->data.length;
419                 }
420                 memcpy(mac->wpa.IE, extra, wrqu->data.length);
421                 dprintk(KERN_INFO PFX "generic IE set to ");
422                 for (i=0;i<wrqu->data.length;i++)
423                         dprintk("%.2x", (u8)mac->wpa.IE[i]);
424                 dprintk("\n");
425                 mac->wpa.IElen = wrqu->data.length;
426         } else {
427                 kfree(mac->wpa.IE);
428                 mac->wpa.IE = NULL;
429                 mac->wpa.IElen = 0;
430                 mac->wpa.IEbuflen = 0;
431         }
432
433  out:   
434         spin_unlock_irqrestore(&mac->lock, flags);
435         return err;
436 }
437 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
438
439 int
440 ieee80211softmac_wx_get_genie(struct net_device *dev,
441                               struct iw_request_info *info,
442                               union iwreq_data *wrqu,
443                               char *extra)
444 {
445         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
446         unsigned long flags;
447         int err = 0;
448         int space = wrqu->data.length;
449         
450         spin_lock_irqsave(&mac->lock, flags);
451         
452         wrqu->data.length = 0;
453         
454         if (mac->wpa.IE && mac->wpa.IElen) {
455                 wrqu->data.length = mac->wpa.IElen;
456                 if (mac->wpa.IElen <= space)
457                         memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
458                 else
459                         err = -E2BIG;
460         }
461         spin_unlock_irqrestore(&mac->lock, flags);
462         return err;
463 }
464 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
465
466 int
467 ieee80211softmac_wx_set_mlme(struct net_device *dev,
468                              struct iw_request_info *info,
469                              union iwreq_data *wrqu,
470                              char *extra)
471 {
472         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
473         struct iw_mlme *mlme = (struct iw_mlme *)extra;
474         u16 reason = cpu_to_le16(mlme->reason_code);
475         struct ieee80211softmac_network *net;
476
477         if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
478                 printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
479                 return -EINVAL;
480         }
481
482         switch (mlme->cmd) {
483         case IW_MLME_DEAUTH:
484                 net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
485                 if (!net) {
486                         printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
487                         return -EINVAL;
488                 }
489                 return ieee80211softmac_deauth_req(mac, net, reason);
490         case IW_MLME_DISASSOC:
491                 ieee80211softmac_send_disassoc_req(mac, reason);
492                 return 0;
493         default:
494                 return -EOPNOTSUPP;
495         }
496 }
497 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);