automake build system
[mech_eap.orig] / src / drivers / driver_broadcom.c
1 /*
2  * WPA Supplicant - driver interaction with old Broadcom wl.o driver
3  * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru>
4  * Copyright (c) 2004, Jouni Malinen <j@w1.fi>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Alternatively, this software may be distributed under the terms of BSD
11  * license.
12  *
13  * See README and COPYING for more details.
14  *
15  * Please note that the newer Broadcom driver ("hybrid Linux driver") supports
16  * Linux wireless extensions and does not need (or even work) with this old
17  * driver wrapper. Use driver_wext.c with that driver.
18  */
19
20 #include "includes.h"
21
22 #include <sys/ioctl.h>
23
24 #include "common.h"
25
26 #if 0
27 #include <netpacket/packet.h>
28 #include <net/ethernet.h>     /* the L2 protocols */
29 #else
30 #include <linux/if_packet.h>
31 #include <linux/if_ether.h>   /* The L2 protocols */
32 #endif
33 #include <net/if.h>
34 #include <typedefs.h>
35
36 /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
37  * WRT54G GPL tarball. */
38 #include <wlioctl.h>
39
40 #include "driver.h"
41 #include "eloop.h"
42
43 struct wpa_driver_broadcom_data {
44         void *ctx;
45         int ioctl_sock;
46         int event_sock;
47         char ifname[IFNAMSIZ + 1];
48 };
49
50
51 #ifndef WLC_DEAUTHENTICATE
52 #define WLC_DEAUTHENTICATE 143
53 #endif
54 #ifndef WLC_DEAUTHENTICATE_WITH_REASON
55 #define WLC_DEAUTHENTICATE_WITH_REASON 201
56 #endif
57 #ifndef WLC_SET_TKIP_COUNTERMEASURES
58 #define WLC_SET_TKIP_COUNTERMEASURES 202
59 #endif
60
61 #if !defined(PSK_ENABLED) /* NEW driver interface */
62 #define WL_VERSION 360130
63 /* wireless authentication bit vector */
64 #define WPA_ENABLED 1
65 #define PSK_ENABLED 2
66                                                                                 
67 #define WAUTH_WPA_ENABLED(wauth)  ((wauth) & WPA_ENABLED)
68 #define WAUTH_PSK_ENABLED(wauth)  ((wauth) & PSK_ENABLED)
69 #define WAUTH_ENABLED(wauth)    ((wauth) & (WPA_ENABLED | PSK_ENABLED))
70
71 #define WSEC_PRIMARY_KEY WL_PRIMARY_KEY
72
73 typedef wl_wsec_key_t wsec_key_t;
74 #endif
75
76 typedef struct {
77         uint32 val;
78         struct ether_addr ea;
79         uint16 res;
80 } wlc_deauth_t;
81
82
83 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
84                                              void *timeout_ctx);
85
86 static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
87                           void *buf, int len)
88 {
89         struct ifreq ifr;
90         wl_ioctl_t ioc;
91         int ret = 0;
92
93         wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)",
94                    drv->ifname, cmd, len, buf);
95         /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */
96
97         ioc.cmd = cmd;
98         ioc.buf = buf;
99         ioc.len = len;
100         os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
101         ifr.ifr_data = (caddr_t) &ioc;
102         if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) {
103                 if (cmd != WLC_GET_MAGIC)
104                         perror(ifr.ifr_name);
105                 wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d",
106                            cmd, ret);
107         }
108
109         return ret;
110 }
111
112 static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid)
113 {
114         struct wpa_driver_broadcom_data *drv = priv;
115         if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0)
116                 return 0;
117         
118         os_memset(bssid, 0, ETH_ALEN);
119         return -1;
120 }
121
122 static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
123 {
124         struct wpa_driver_broadcom_data *drv = priv;
125         wlc_ssid_t s;
126         
127         if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
128                 return -1;
129
130         os_memcpy(ssid, s.SSID, s.SSID_len);
131         return s.SSID_len;
132 }
133
134 static int wpa_driver_broadcom_set_wpa(void *priv, int enable)
135 {
136         struct wpa_driver_broadcom_data *drv = priv;
137         unsigned int wauth, wsec;
138         struct ether_addr ea;
139
140         os_memset(&ea, enable ? 0xff : 0, sizeof(ea));
141         if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) ==
142             -1 ||
143             broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1)
144                 return -1;
145
146         if (enable) {
147                 wauth = PSK_ENABLED;
148                 wsec = TKIP_ENABLED;
149         } else {
150                 wauth = 255;
151                 wsec &= ~(TKIP_ENABLED | AES_ENABLED);
152         }
153
154         if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) ==
155             -1 ||
156             broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1)
157                 return -1;
158
159         /* FIX: magic number / error handling? */
160         broadcom_ioctl(drv, 122, &ea, sizeof(ea));
161
162         return 0;
163 }
164
165 static int wpa_driver_broadcom_set_key(const char *ifname, void *priv,
166                                        enum wpa_alg alg,
167                                        const u8 *addr, int key_idx, int set_tx,
168                                        const u8 *seq, size_t seq_len,
169                                        const u8 *key, size_t key_len)
170 {
171         struct wpa_driver_broadcom_data *drv = priv;
172         int ret;
173         wsec_key_t wkt;
174
175         os_memset(&wkt, 0, sizeof wkt);
176         wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d",
177                    set_tx ? "PRIMARY " : "", key_idx, alg);
178         if (key && key_len > 0)
179                 wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len);
180
181         switch (alg) {
182         case WPA_ALG_NONE:
183                 wkt.algo = CRYPTO_ALGO_OFF;
184                 break;
185         case WPA_ALG_WEP:
186                 wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
187                 break;
188         case WPA_ALG_TKIP:
189                 wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
190                 break;
191         case WPA_ALG_CCMP:
192                 wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
193                                * AES_OCB_MSDU, AES_OCB_MPDU? */
194                 break;
195         default:
196                 wkt.algo = CRYPTO_ALGO_NALG;
197                 break;
198         }
199
200         if (seq && seq_len > 0)
201                 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
202
203         if (addr)
204                 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
205
206         wkt.index = key_idx;
207         wkt.len = key_len;
208         if (key && key_len > 0) {
209                 os_memcpy(wkt.data, key, key_len);
210                 if (key_len == 32) {
211                         /* hack hack hack XXX */
212                         os_memcpy(&wkt.data[16], &key[24], 8);
213                         os_memcpy(&wkt.data[24], &key[16], 8);
214                 }
215         }
216         /* wkt.algo = CRYPTO_ALGO_...; */
217         wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
218         if (addr && set_tx)
219                 os_memcpy(&wkt.ea, addr, sizeof(wkt.ea));
220         ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt));
221         if (addr && set_tx) {
222                 /* FIX: magic number / error handling? */
223                 broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea));
224         }
225         return ret;
226 }
227
228
229 static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
230                                               void *sock_ctx)
231 {
232         char buf[8192];
233         int left;
234         wl_wpa_header_t *wwh;
235         union wpa_event_data data;
236         u8 *resp_ies = NULL;
237
238         if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
239                 return;
240
241         wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
242
243         if ((size_t) left < sizeof(wl_wpa_header_t))
244                 return;
245
246         wwh = (wl_wpa_header_t *) buf;
247
248         if (wwh->snap.type != WL_WPA_ETHER_TYPE)
249                 return;
250         if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
251                 return;
252
253         os_memset(&data, 0, sizeof(data));
254
255         switch (wwh->type) {
256         case WLC_ASSOC_MSG:
257                 left -= WL_WPA_HEADER_LEN;
258                 wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
259                            left);
260                 if (left > 0) {
261                         resp_ies = os_malloc(left);
262                         if (resp_ies == NULL)
263                                 return;
264                         os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left);
265                         data.assoc_info.resp_ies = resp_ies;
266                         data.assoc_info.resp_ies_len = left;
267                 }
268
269                 wpa_supplicant_event(ctx, EVENT_ASSOC, &data);
270                 os_free(resp_ies);
271                 break;
272         case WLC_DISASSOC_MSG:
273                 wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
274                 wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
275                 break;
276         case WLC_PTK_MIC_MSG:
277                 wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE");
278                 data.michael_mic_failure.unicast = 1;
279                 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
280                 break;
281         case WLC_GTK_MIC_MSG:
282                 wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE");
283                 data.michael_mic_failure.unicast = 0;
284                 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
285                 break;
286         default:
287                 wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
288                            wwh->type);
289                 break;
290         }
291 }       
292
293 static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
294 {
295         int s;
296         struct sockaddr_ll ll;
297         struct wpa_driver_broadcom_data *drv;
298         struct ifreq ifr;
299
300         /* open socket to kernel */
301         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
302                 perror("socket");
303                 return NULL;
304         }
305         /* do it */
306         os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
307         if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
308                 perror(ifr.ifr_name);
309                 return NULL;
310         }
311
312
313         drv = os_zalloc(sizeof(*drv));
314         if (drv == NULL)
315                 return NULL;
316         drv->ctx = ctx;
317         os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
318         drv->ioctl_sock = s;
319
320         s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
321         if (s < 0) {
322                 perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
323                 close(drv->ioctl_sock);
324                 os_free(drv);
325                 return NULL;
326         }
327
328         os_memset(&ll, 0, sizeof(ll));
329         ll.sll_family = AF_PACKET;
330         ll.sll_protocol = ntohs(ETH_P_802_2);
331         ll.sll_ifindex = ifr.ifr_ifindex;
332         ll.sll_hatype = 0;
333         ll.sll_pkttype = PACKET_HOST;
334         ll.sll_halen = 0;
335
336         if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
337                 perror("bind(netlink)");
338                 close(s);
339                 close(drv->ioctl_sock);
340                 os_free(drv);
341                 return NULL;
342         }
343
344         eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
345                                  NULL);
346         drv->event_sock = s;
347         wpa_driver_broadcom_set_wpa(drv, 1);
348
349         return drv;
350 }
351
352 static void wpa_driver_broadcom_deinit(void *priv)
353 {
354         struct wpa_driver_broadcom_data *drv = priv;
355         wpa_driver_broadcom_set_wpa(drv, 0);
356         eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
357         eloop_unregister_read_sock(drv->event_sock);
358         close(drv->event_sock);
359         close(drv->ioctl_sock);
360         os_free(drv);
361 }
362
363 static int wpa_driver_broadcom_set_countermeasures(void *priv,
364                                                    int enabled)
365 {
366 #if 0
367         struct wpa_driver_broadcom_data *drv = priv;
368         /* FIX: ? */
369         return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
370                               sizeof(enabled));
371 #else
372         return 0;
373 #endif
374 }
375
376 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
377 {
378         struct wpa_driver_broadcom_data *drv = priv;
379         /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
380         int _restrict = (enabled ? 1 : 0);
381         
382         if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, 
383                            &_restrict, sizeof(_restrict)) < 0 ||
384             broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT,
385                            &_restrict, sizeof(_restrict)) < 0)
386                 return -1;
387
388         return 0;
389 }
390
391 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
392                                              void *timeout_ctx)
393 {
394         wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
395         wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
396 }
397
398 static int wpa_driver_broadcom_scan(void *priv,
399                                     struct wpa_driver_scan_params *params)
400 {
401         struct wpa_driver_broadcom_data *drv = priv;
402         wlc_ssid_t wst = { 0, "" };
403         const u8 *ssid = params->ssids[0].ssid;
404         size_t ssid_len = params->ssids[0].ssid_len;
405
406         if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) {
407                 wst.SSID_len = ssid_len;
408                 os_memcpy(wst.SSID, ssid, ssid_len);
409         }
410         
411         if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0)
412                 return -1;
413
414         eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
415         eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv,
416                                drv->ctx);
417         return 0;
418 }
419
420
421 static const int frequency_list[] = { 
422         2412, 2417, 2422, 2427, 2432, 2437, 2442,
423         2447, 2452, 2457, 2462, 2467, 2472, 2484 
424 };
425
426 struct bss_ie_hdr {
427         u8 elem_id;
428         u8 len;
429         u8 oui[3];
430         /* u8 oui_type; */
431         /* u16 version; */
432 } __attribute__ ((packed));
433
434 static struct wpa_scan_results *
435 wpa_driver_broadcom_get_scan_results(void *priv)
436 {
437         struct wpa_driver_broadcom_data *drv = priv;
438         char *buf;
439         wl_scan_results_t *wsr;
440         wl_bss_info_t *wbi;
441         size_t ap_num;
442         struct wpa_scan_results *res;
443
444         buf = os_malloc(WLC_IOCTL_MAXLEN);
445         if (buf == NULL)
446                 return NULL;
447
448         wsr = (wl_scan_results_t *) buf;
449
450         wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
451         wsr->version = 107;
452         wsr->count = 0;
453
454         if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
455                 os_free(buf);
456                 return NULL;
457         }
458
459         res = os_zalloc(sizeof(*res));
460         if (res == NULL) {
461                 os_free(buf);
462                 return NULL;
463         }
464
465         res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *));
466         if (res->res == NULL) {
467                 os_free(res);
468                 os_free(buf);
469                 return NULL;
470         }
471
472         for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
473                 struct wpa_scan_res *r;
474                 r = os_malloc(sizeof(*r) + wbi->ie_length);
475                 if (r == NULL)
476                         break;
477                 res->res[res->num++] = r;
478
479                 os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN);
480                 r->freq = frequency_list[wbi->channel - 1];
481                 /* get ie's */
482                 os_memcpy(r + 1, wbi + 1, wbi->ie_length);
483                 r->ie_len = wbi->ie_length;
484
485                 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
486         }
487
488         wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
489                    "BSSes)",
490                    wsr->buflen, (unsigned long) ap_num);
491         
492         os_free(buf);
493         return res;
494         }
495
496 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
497                                               int reason_code)
498 {
499         struct wpa_driver_broadcom_data *drv = priv;
500         wlc_deauth_t wdt;
501         wdt.val = reason_code;
502         os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
503         wdt.res = 0x7fff;
504         return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
505                               sizeof(wdt));
506 }
507
508 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
509                                             int reason_code)
510 {
511         struct wpa_driver_broadcom_data *drv = priv;
512         return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0);
513 }
514
515 static int
516 wpa_driver_broadcom_associate(void *priv,
517                               struct wpa_driver_associate_params *params)
518 {
519         struct wpa_driver_broadcom_data *drv = priv;
520         wlc_ssid_t s;
521         int infra = 1;
522         int auth = 0;
523         int wsec = 4;
524         int dummy;
525         int wpa_auth;
526         int ret;
527
528         ret = wpa_driver_broadcom_set_drop_unencrypted(
529                 drv, params->drop_unencrypted);
530
531         s.SSID_len = params->ssid_len;
532         os_memcpy(s.SSID, params->ssid, params->ssid_len);
533
534         switch (params->pairwise_suite) {
535         case CIPHER_WEP40:
536         case CIPHER_WEP104:
537                 wsec = 1;
538                 break;
539
540         case CIPHER_TKIP:
541                 wsec = 2;
542                 break;
543
544         case CIPHER_CCMP:
545                 wsec = 4;
546                 break;
547
548         default:
549                 wsec = 0;
550                 break;
551         }
552
553         switch (params->key_mgmt_suite) {
554         case KEY_MGMT_802_1X:
555                 wpa_auth = 1;
556                 break;
557
558         case KEY_MGMT_PSK:
559                 wpa_auth = 2;
560                 break;
561
562         default:
563                 wpa_auth = 255;
564                 break;
565         }
566
567         /* printf("broadcom_associate: %u %u %u\n", pairwise_suite,
568          * group_suite, key_mgmt_suite);
569          * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec));
570          * wl join uses wlc_sec_wep here, not wlc_set_wsec */
571
572         if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 ||
573             broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth,
574                            sizeof(wpa_auth)) < 0 ||
575             broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 ||
576             broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 ||
577             broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 ||
578             broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 ||
579             broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0)
580                 return -1;
581
582         return ret;
583 }
584
585 const struct wpa_driver_ops wpa_driver_broadcom_ops = {
586         .name = "broadcom",
587         .desc = "Broadcom wl.o driver",
588         .get_bssid = wpa_driver_broadcom_get_bssid,
589         .get_ssid = wpa_driver_broadcom_get_ssid,
590         .set_key = wpa_driver_broadcom_set_key,
591         .init = wpa_driver_broadcom_init,
592         .deinit = wpa_driver_broadcom_deinit,
593         .set_countermeasures = wpa_driver_broadcom_set_countermeasures,
594         .scan2 = wpa_driver_broadcom_scan,
595         .get_scan_results2 = wpa_driver_broadcom_get_scan_results,
596         .deauthenticate = wpa_driver_broadcom_deauthenticate,
597         .disassociate = wpa_driver_broadcom_disassociate,
598         .associate = wpa_driver_broadcom_associate,
599 };