cfg80211: keep track of BSSes
[pandora-kernel.git] / net / wireless / mlme.c
1 /*
2  * cfg80211 MLME SAP interface
3  *
4  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
5  */
6
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/netdevice.h>
10 #include <linux/nl80211.h>
11 #include <net/cfg80211.h>
12 #include "core.h"
13 #include "nl80211.h"
14
15 void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
16 {
17         struct wireless_dev *wdev = dev->ieee80211_ptr;
18         struct wiphy *wiphy = wdev->wiphy;
19         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
20         struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
21         u8 *bssid = mgmt->bssid;
22         int i;
23         u16 status = le16_to_cpu(mgmt->u.auth.status_code);
24         bool done = false;
25
26         for (i = 0; i < MAX_AUTH_BSSES; i++) {
27                 if (wdev->authtry_bsses[i] &&
28                     memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
29                                                         ETH_ALEN) == 0) {
30                         if (status == WLAN_STATUS_SUCCESS) {
31                                 wdev->auth_bsses[i] = wdev->authtry_bsses[i];
32                         } else {
33                                 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
34                                 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
35                         }
36                         wdev->authtry_bsses[i] = NULL;
37                         done = true;
38                         break;
39                 }
40         }
41
42         WARN_ON(!done);
43
44         nl80211_send_rx_auth(rdev, dev, buf, len, gfp);
45         cfg80211_sme_rx_auth(dev, buf, len);
46 }
47 EXPORT_SYMBOL(cfg80211_send_rx_auth);
48
49 void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
50 {
51         u16 status_code;
52         struct wireless_dev *wdev = dev->ieee80211_ptr;
53         struct wiphy *wiphy = wdev->wiphy;
54         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
55         struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
56         u8 *ie = mgmt->u.assoc_resp.variable;
57         int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
58         bool done;
59
60         status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
61
62         nl80211_send_rx_assoc(rdev, dev, buf, len, gfp);
63
64         cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
65                                 status_code, gfp);
66
67         if (status_code == WLAN_STATUS_SUCCESS) {
68                 for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) {
69                         if (wdev->auth_bsses[i] == wdev->current_bss) {
70                                 cfg80211_unhold_bss(wdev->auth_bsses[i]);
71                                 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
72                                 wdev->auth_bsses[i] = NULL;
73                                 done = true;
74                                 break;
75                         }
76                 }
77
78                 WARN_ON(!done);
79         }
80 }
81 EXPORT_SYMBOL(cfg80211_send_rx_assoc);
82
83 void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
84 {
85         struct wireless_dev *wdev = dev->ieee80211_ptr;
86         struct wiphy *wiphy = wdev->wiphy;
87         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
88         struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
89         const u8 *bssid = mgmt->bssid;
90         int i;
91         bool done = false;
92
93         nl80211_send_deauth(rdev, dev, buf, len, gfp);
94
95         if (wdev->current_bss &&
96             memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
97                 done = true;
98                 cfg80211_unhold_bss(wdev->current_bss);
99                 cfg80211_put_bss(&wdev->current_bss->pub);
100                 wdev->current_bss = NULL;
101         } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
102                 if (wdev->auth_bsses[i] &&
103                     memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
104                         cfg80211_unhold_bss(wdev->auth_bsses[i]);
105                         cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
106                         wdev->auth_bsses[i] = NULL;
107                         done = true;
108                         break;
109                 }
110                 if (wdev->authtry_bsses[i] &&
111                     memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
112                         cfg80211_unhold_bss(wdev->authtry_bsses[i]);
113                         cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
114                         wdev->authtry_bsses[i] = NULL;
115                         done = true;
116                         break;
117                 }
118         }
119 /*
120  * mac80211 currently triggers this warning,
121  * so disable for now (it's harmless, just
122  * means that we got a spurious event)
123
124         WARN_ON(!done);
125
126  */
127
128         if (wdev->sme_state == CFG80211_SME_CONNECTED) {
129                 u16 reason_code;
130                 bool from_ap;
131
132                 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
133
134                 from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
135                 __cfg80211_disconnected(dev, gfp, NULL, 0,
136                                         reason_code, from_ap);
137         } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
138                 cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
139                                         WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
140         }
141 }
142 EXPORT_SYMBOL(cfg80211_send_deauth);
143
144 void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
145 {
146         struct wireless_dev *wdev = dev->ieee80211_ptr;
147         struct wiphy *wiphy = wdev->wiphy;
148         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
149         struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
150         const u8 *bssid = mgmt->bssid;
151         int i;
152         u16 reason_code;
153         bool from_ap;
154         bool done = false;
155
156         nl80211_send_disassoc(rdev, dev, buf, len, gfp);
157
158         if (!wdev->sme_state == CFG80211_SME_CONNECTED)
159                 return;
160
161         if (wdev->current_bss &&
162             memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) {
163                 for (i = 0; i < MAX_AUTH_BSSES; i++) {
164                         if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
165                                 continue;
166                         wdev->auth_bsses[i] = wdev->current_bss;
167                         wdev->current_bss = NULL;
168                         done = true;
169                         cfg80211_sme_disassoc(dev, i);
170                         break;
171                 }
172                 WARN_ON(!done);
173         } else
174                 WARN_ON(1);
175
176
177         reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
178
179         from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
180         __cfg80211_disconnected(dev, gfp, NULL, 0,
181                                 reason_code, from_ap);
182 }
183 EXPORT_SYMBOL(cfg80211_send_disassoc);
184
185 void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp)
186 {
187         struct wireless_dev *wdev = dev->ieee80211_ptr;
188         struct wiphy *wiphy = wdev->wiphy;
189         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
190         int i;
191         bool done = false;
192
193         nl80211_send_auth_timeout(rdev, dev, addr, gfp);
194         if (wdev->sme_state == CFG80211_SME_CONNECTING)
195                 cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
196                                         WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
197
198         for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
199                 if (wdev->authtry_bsses[i] &&
200                     memcmp(wdev->authtry_bsses[i]->pub.bssid,
201                            addr, ETH_ALEN) == 0) {
202                         cfg80211_unhold_bss(wdev->authtry_bsses[i]);
203                         cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
204                         wdev->authtry_bsses[i] = NULL;
205                         done = true;
206                         break;
207                 }
208         }
209
210         WARN_ON(!done);
211 }
212 EXPORT_SYMBOL(cfg80211_send_auth_timeout);
213
214 void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp)
215 {
216         struct wireless_dev *wdev = dev->ieee80211_ptr;
217         struct wiphy *wiphy = wdev->wiphy;
218         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
219         int i;
220         bool done = false;
221
222         nl80211_send_assoc_timeout(rdev, dev, addr, gfp);
223         if (wdev->sme_state == CFG80211_SME_CONNECTING)
224                 cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
225                                         WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
226
227         for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
228                 if (wdev->auth_bsses[i] &&
229                     memcmp(wdev->auth_bsses[i]->pub.bssid,
230                            addr, ETH_ALEN) == 0) {
231                         cfg80211_unhold_bss(wdev->auth_bsses[i]);
232                         cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
233                         wdev->auth_bsses[i] = NULL;
234                         done = true;
235                         break;
236                 }
237         }
238
239         WARN_ON(!done);
240 }
241 EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
242
243 void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
244                                   enum nl80211_key_type key_type, int key_id,
245                                   const u8 *tsc, gfp_t gfp)
246 {
247         struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
248         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
249 #ifdef CONFIG_WIRELESS_EXT
250         union iwreq_data wrqu;
251         char *buf = kmalloc(128, gfp);
252
253         if (buf) {
254                 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
255                         "keyid=%d %scast addr=%pM)", key_id,
256                         key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
257                         addr);
258                 memset(&wrqu, 0, sizeof(wrqu));
259                 wrqu.data.length = strlen(buf);
260                 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
261                 kfree(buf);
262         }
263 #endif
264
265         nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
266 }
267 EXPORT_SYMBOL(cfg80211_michael_mic_failure);
268
269 /* some MLME handling for userspace SME */
270 int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
271                        struct net_device *dev, struct ieee80211_channel *chan,
272                        enum nl80211_auth_type auth_type, const u8 *bssid,
273                        const u8 *ssid, int ssid_len,
274                        const u8 *ie, int ie_len)
275 {
276         struct wireless_dev *wdev = dev->ieee80211_ptr;
277         struct cfg80211_auth_request req;
278         struct cfg80211_internal_bss *bss;
279         int i, err, slot = -1, nfree = 0;
280
281         memset(&req, 0, sizeof(req));
282
283         req.ie = ie;
284         req.ie_len = ie_len;
285         req.auth_type = auth_type;
286         req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
287                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
288         if (!req.bss)
289                 return -ENOENT;
290
291         bss = bss_from_pub(req.bss);
292
293         for (i = 0; i < MAX_AUTH_BSSES; i++) {
294                 if (bss == wdev->auth_bsses[i]) {
295                         err = -EALREADY;
296                         goto out;
297                 }
298         }
299
300         for (i = 0; i < MAX_AUTH_BSSES; i++) {
301                 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
302                         slot = i;
303                         nfree++;
304                 }
305         }
306
307         /* we need one free slot for disassoc and one for this auth */
308         if (nfree < 2) {
309                 err = -ENOSPC;
310                 goto out;
311         }
312
313         wdev->authtry_bsses[slot] = bss;
314         cfg80211_hold_bss(bss);
315
316         err = rdev->ops->auth(&rdev->wiphy, dev, &req);
317         if (err) {
318                 wdev->authtry_bsses[slot] = NULL;
319                 cfg80211_unhold_bss(bss);
320         }
321
322  out:
323         if (err)
324                 cfg80211_put_bss(req.bss);
325         return err;
326 }
327
328 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
329                         struct net_device *dev, struct ieee80211_channel *chan,
330                         const u8 *bssid, const u8 *ssid, int ssid_len,
331                         const u8 *ie, int ie_len, bool use_mfp,
332                         struct cfg80211_crypto_settings *crypt)
333 {
334         struct wireless_dev *wdev = dev->ieee80211_ptr;
335         struct cfg80211_assoc_request req;
336         struct cfg80211_internal_bss *bss;
337         int i, err, slot = -1;
338
339         memset(&req, 0, sizeof(req));
340
341         if (wdev->current_bss)
342                 return -EALREADY;
343
344         req.ie = ie;
345         req.ie_len = ie_len;
346         memcpy(&req.crypto, crypt, sizeof(req.crypto));
347         req.use_mfp = use_mfp;
348         req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
349                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
350         if (!req.bss)
351                 return -ENOENT;
352
353         bss = bss_from_pub(req.bss);
354
355         for (i = 0; i < MAX_AUTH_BSSES; i++) {
356                 if (bss == wdev->auth_bsses[i]) {
357                         slot = i;
358                         break;
359                 }
360         }
361
362         if (slot < 0) {
363                 err = -ENOTCONN;
364                 goto out;
365         }
366
367         err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
368  out:
369         /* still a reference in wdev->auth_bsses[slot] */
370         cfg80211_put_bss(req.bss);
371         return err;
372 }
373
374 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
375                          struct net_device *dev, const u8 *bssid,
376                          const u8 *ie, int ie_len, u16 reason)
377 {
378         struct wireless_dev *wdev = dev->ieee80211_ptr;
379         struct cfg80211_deauth_request req;
380         int i;
381
382         memset(&req, 0, sizeof(req));
383         req.reason_code = reason;
384         req.ie = ie;
385         req.ie_len = ie_len;
386         if (wdev->current_bss &&
387             memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
388                 req.bss = &wdev->current_bss->pub;
389         } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
390                 if (wdev->auth_bsses[i] &&
391                     memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
392                         req.bss = &wdev->auth_bsses[i]->pub;
393                         break;
394                 }
395                 if (wdev->authtry_bsses[i] &&
396                     memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
397                         req.bss = &wdev->authtry_bsses[i]->pub;
398                         break;
399                 }
400         }
401
402         if (!req.bss)
403                 return -ENOTCONN;
404
405         return rdev->ops->deauth(&rdev->wiphy, dev, &req);
406 }
407
408 int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
409                            struct net_device *dev, const u8 *bssid,
410                            const u8 *ie, int ie_len, u16 reason)
411 {
412         struct wireless_dev *wdev = dev->ieee80211_ptr;
413         struct cfg80211_disassoc_request req;
414
415         memset(&req, 0, sizeof(req));
416         req.reason_code = reason;
417         req.ie = ie;
418         req.ie_len = ie_len;
419         if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
420                 req.bss = &wdev->current_bss->pub;
421         else
422                 return -ENOTCONN;
423
424         return rdev->ops->disassoc(&rdev->wiphy, dev, &req);
425 }
426
427 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
428                         struct net_device *dev)
429 {
430         struct wireless_dev *wdev = dev->ieee80211_ptr;
431         struct cfg80211_deauth_request req;
432         int i;
433
434         if (!rdev->ops->deauth)
435                 return;
436
437         memset(&req, 0, sizeof(req));
438         req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
439         req.ie = NULL;
440         req.ie_len = 0;
441
442         if (wdev->current_bss) {
443                 req.bss = &wdev->current_bss->pub;
444                 rdev->ops->deauth(&rdev->wiphy, dev, &req);
445                 if (wdev->current_bss) {
446                         cfg80211_unhold_bss(wdev->current_bss);
447                         cfg80211_put_bss(&wdev->current_bss->pub);
448                         wdev->current_bss = NULL;
449                 }
450         }
451
452         for (i = 0; i < MAX_AUTH_BSSES; i++) {
453                 if (wdev->auth_bsses[i]) {
454                         req.bss = &wdev->auth_bsses[i]->pub;
455                         rdev->ops->deauth(&rdev->wiphy, dev, &req);
456                         if (wdev->auth_bsses[i]) {
457                                 cfg80211_unhold_bss(wdev->auth_bsses[i]);
458                                 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
459                                 wdev->auth_bsses[i] = NULL;
460                         }
461                 }
462                 if (wdev->authtry_bsses[i]) {
463                         req.bss = &wdev->authtry_bsses[i]->pub;
464                         rdev->ops->deauth(&rdev->wiphy, dev, &req);
465                         if (wdev->authtry_bsses[i]) {
466                                 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
467                                 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
468                                 wdev->authtry_bsses[i] = NULL;
469                         }
470                 }
471         }
472 }