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