cfg80211: let SME control reassociation vs. association
[safe/jmp/linux-2.6] / 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         if (wdev->current_bss &&
282             memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
283                 return -EALREADY;
284
285         for (i = 0; i < MAX_AUTH_BSSES; i++) {
286                 if (wdev->authtry_bsses[i] &&
287                     memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
288                                                 ETH_ALEN) == 0)
289                         return -EALREADY;
290                 if (wdev->auth_bsses[i] &&
291                     memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
292                                                 ETH_ALEN) == 0)
293                         return -EALREADY;
294         }
295
296         memset(&req, 0, sizeof(req));
297
298         req.ie = ie;
299         req.ie_len = ie_len;
300         req.auth_type = auth_type;
301         req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
302                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
303         if (!req.bss)
304                 return -ENOENT;
305
306         bss = bss_from_pub(req.bss);
307
308         for (i = 0; i < MAX_AUTH_BSSES; i++) {
309                 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
310                         slot = i;
311                         nfree++;
312                 }
313         }
314
315         /* we need one free slot for disassoc and one for this auth */
316         if (nfree < 2) {
317                 err = -ENOSPC;
318                 goto out;
319         }
320
321         wdev->authtry_bsses[slot] = bss;
322         cfg80211_hold_bss(bss);
323
324         err = rdev->ops->auth(&rdev->wiphy, dev, &req);
325         if (err) {
326                 wdev->authtry_bsses[slot] = NULL;
327                 cfg80211_unhold_bss(bss);
328         }
329
330  out:
331         if (err)
332                 cfg80211_put_bss(req.bss);
333         return err;
334 }
335
336 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
337                         struct net_device *dev, struct ieee80211_channel *chan,
338                         const u8 *bssid, const u8 *prev_bssid,
339                         const u8 *ssid, int ssid_len,
340                         const u8 *ie, int ie_len, bool use_mfp,
341                         struct cfg80211_crypto_settings *crypt)
342 {
343         struct wireless_dev *wdev = dev->ieee80211_ptr;
344         struct cfg80211_assoc_request req;
345         struct cfg80211_internal_bss *bss;
346         int i, err, slot = -1;
347
348         memset(&req, 0, sizeof(req));
349
350         if (wdev->current_bss)
351                 return -EALREADY;
352
353         req.ie = ie;
354         req.ie_len = ie_len;
355         memcpy(&req.crypto, crypt, sizeof(req.crypto));
356         req.use_mfp = use_mfp;
357         req.prev_bssid = prev_bssid;
358         req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
359                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
360         if (!req.bss)
361                 return -ENOENT;
362
363         bss = bss_from_pub(req.bss);
364
365         for (i = 0; i < MAX_AUTH_BSSES; i++) {
366                 if (bss == wdev->auth_bsses[i]) {
367                         slot = i;
368                         break;
369                 }
370         }
371
372         if (slot < 0) {
373                 err = -ENOTCONN;
374                 goto out;
375         }
376
377         err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
378  out:
379         /* still a reference in wdev->auth_bsses[slot] */
380         cfg80211_put_bss(req.bss);
381         return err;
382 }
383
384 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
385                          struct net_device *dev, const u8 *bssid,
386                          const u8 *ie, int ie_len, u16 reason)
387 {
388         struct wireless_dev *wdev = dev->ieee80211_ptr;
389         struct cfg80211_deauth_request req;
390         int i;
391
392         memset(&req, 0, sizeof(req));
393         req.reason_code = reason;
394         req.ie = ie;
395         req.ie_len = ie_len;
396         if (wdev->current_bss &&
397             memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
398                 req.bss = &wdev->current_bss->pub;
399         } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
400                 if (wdev->auth_bsses[i] &&
401                     memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
402                         req.bss = &wdev->auth_bsses[i]->pub;
403                         break;
404                 }
405                 if (wdev->authtry_bsses[i] &&
406                     memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
407                         req.bss = &wdev->authtry_bsses[i]->pub;
408                         break;
409                 }
410         }
411
412         if (!req.bss)
413                 return -ENOTCONN;
414
415         return rdev->ops->deauth(&rdev->wiphy, dev, &req);
416 }
417
418 int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
419                            struct net_device *dev, const u8 *bssid,
420                            const u8 *ie, int ie_len, u16 reason)
421 {
422         struct wireless_dev *wdev = dev->ieee80211_ptr;
423         struct cfg80211_disassoc_request req;
424
425         memset(&req, 0, sizeof(req));
426         req.reason_code = reason;
427         req.ie = ie;
428         req.ie_len = ie_len;
429         if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
430                 req.bss = &wdev->current_bss->pub;
431         else
432                 return -ENOTCONN;
433
434         return rdev->ops->disassoc(&rdev->wiphy, dev, &req);
435 }
436
437 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
438                         struct net_device *dev)
439 {
440         struct wireless_dev *wdev = dev->ieee80211_ptr;
441         struct cfg80211_deauth_request req;
442         int i;
443
444         if (!rdev->ops->deauth)
445                 return;
446
447         memset(&req, 0, sizeof(req));
448         req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
449         req.ie = NULL;
450         req.ie_len = 0;
451
452         if (wdev->current_bss) {
453                 req.bss = &wdev->current_bss->pub;
454                 rdev->ops->deauth(&rdev->wiphy, dev, &req);
455                 if (wdev->current_bss) {
456                         cfg80211_unhold_bss(wdev->current_bss);
457                         cfg80211_put_bss(&wdev->current_bss->pub);
458                         wdev->current_bss = NULL;
459                 }
460         }
461
462         for (i = 0; i < MAX_AUTH_BSSES; i++) {
463                 if (wdev->auth_bsses[i]) {
464                         req.bss = &wdev->auth_bsses[i]->pub;
465                         rdev->ops->deauth(&rdev->wiphy, dev, &req);
466                         if (wdev->auth_bsses[i]) {
467                                 cfg80211_unhold_bss(wdev->auth_bsses[i]);
468                                 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
469                                 wdev->auth_bsses[i] = NULL;
470                         }
471                 }
472                 if (wdev->authtry_bsses[i]) {
473                         req.bss = &wdev->authtry_bsses[i]->pub;
474                         rdev->ops->deauth(&rdev->wiphy, dev, &req);
475                         if (wdev->authtry_bsses[i]) {
476                                 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
477                                 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
478                                 wdev->authtry_bsses[i] = NULL;
479                         }
480                 }
481         }
482 }