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