Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / wpa / src / drivers / driver_broadcom.c
blob9a465ca54a003c53f73fbf67749c7c82bd3ed3e9
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>
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.
10 * Alternatively, this software may be distributed under the terms of BSD
11 * license.
13 * See README and COPYING for more details.
16 #include "includes.h"
18 #include <sys/ioctl.h>
20 #include "common.h"
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>
32 /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
33 * WRT54G GPL tarball. */
34 #include <wlioctl.h>
36 #include "driver.h"
37 #include "eloop.h"
39 struct wpa_driver_broadcom_data {
40 void *ctx;
41 int ioctl_sock;
42 int event_sock;
43 char ifname[IFNAMSIZ + 1];
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
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
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))
67 #define WSEC_PRIMARY_KEY WL_PRIMARY_KEY
69 typedef wl_wsec_key_t wsec_key_t;
70 #endif
72 typedef struct {
73 uint32 val;
74 struct ether_addr ea;
75 uint16 res;
76 } wlc_deauth_t;
79 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
80 void *timeout_ctx);
82 static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
83 void *buf, int len)
85 struct ifreq ifr;
86 wl_ioctl_t ioc;
87 int ret = 0;
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); */
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);
105 return ret;
108 static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid)
110 struct wpa_driver_broadcom_data *drv = priv;
111 if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0)
112 return 0;
114 os_memset(bssid, 0, ETH_ALEN);
115 return -1;
118 static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
120 struct wpa_driver_broadcom_data *drv = priv;
121 wlc_ssid_t s;
123 if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
124 return -1;
126 os_memcpy(ssid, s.SSID, s.SSID_len);
127 return s.SSID_len;
130 static int wpa_driver_broadcom_set_wpa(void *priv, int enable)
132 struct wpa_driver_broadcom_data *drv = priv;
133 unsigned int wauth, wsec;
134 struct ether_addr ea;
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;
142 if (enable) {
143 wauth = PSK_ENABLED;
144 wsec = TKIP_ENABLED;
145 } else {
146 wauth = 255;
147 wsec &= ~(TKIP_ENABLED | AES_ENABLED);
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;
155 /* FIX: magic number / error handling? */
156 broadcom_ioctl(drv, 122, &ea, sizeof(ea));
158 return 0;
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)
166 struct wpa_driver_broadcom_data *drv = priv;
167 int ret;
168 wsec_key_t wkt;
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);
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;
195 if (seq && seq_len > 0)
196 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
198 if (addr)
199 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
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);
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));
220 return ret;
224 static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
225 void *sock_ctx)
227 char buf[8192];
228 int left;
229 wl_wpa_header_t *wwh;
230 union wpa_event_data data;
232 if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
233 return;
235 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
237 if ((size_t) left < sizeof(wl_wpa_header_t))
238 return;
240 wwh = (wl_wpa_header_t *) buf;
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;
247 os_memset(&data, 0, sizeof(data));
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);
265 /* data.assoc_info.req_ies = NULL; */
266 /* data.assoc_info.req_ies_len = 0; */
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;
290 os_free(data.assoc_info.resp_ies);
293 static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
295 int s;
296 struct sockaddr_ll ll;
297 struct wpa_driver_broadcom_data *drv;
298 struct ifreq ifr;
300 /* open socket to kernel */
301 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
302 perror("socket");
303 return NULL;
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;
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;
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;
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;
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;
344 eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
345 NULL);
346 drv->event_sock = s;
348 return drv;
351 static void wpa_driver_broadcom_deinit(void *priv)
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);
361 static int wpa_driver_broadcom_set_countermeasures(void *priv,
362 int enabled)
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
374 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
376 struct wpa_driver_broadcom_data *drv = priv;
377 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
378 int restrict = (enabled ? 1 : 0);
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;
386 return 0;
389 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
390 void *timeout_ctx)
392 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
393 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
396 static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid,
397 size_t ssid_len)
399 struct wpa_driver_broadcom_data *drv = priv;
400 wlc_ssid_t wst = { 0, "" };
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);
407 if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0)
408 return -1;
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;
417 static const int frequency_list[] = {
418 2412, 2417, 2422, 2427, 2432, 2437, 2442,
419 2447, 2452, 2457, 2462, 2467, 2472, 2484
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));
430 static int
431 wpa_driver_broadcom_get_scan_results(void *priv,
432 struct wpa_scan_result *results,
433 size_t max_size)
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;
441 buf = os_malloc(WLC_IOCTL_MAXLEN);
442 if (buf == NULL)
443 return -1;
445 wsr = (wl_scan_results_t *) buf;
447 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
448 wsr->version = 107;
449 wsr->count = 0;
451 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
452 os_free(buf);
453 return -1;
456 os_memset(results, 0, max_size * sizeof(struct wpa_scan_result));
458 for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
459 int left;
460 struct bss_ie_hdr *ie;
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;
488 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
491 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%d BSSes)",
492 wsr->buflen, ap_num);
494 os_free(buf);
495 return ap_num;
498 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
499 int reason_code)
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));
510 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
511 int reason_code)
513 struct wpa_driver_broadcom_data *drv = priv;
514 return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0);
517 static int
518 wpa_driver_broadcom_associate(void *priv,
519 struct wpa_driver_associate_params *params)
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;
529 s.SSID_len = params->ssid_len;
530 os_memcpy(s.SSID, params->ssid, params->ssid_len);
532 switch (params->pairwise_suite) {
533 case CIPHER_WEP40:
534 case CIPHER_WEP104:
535 wsec = 1;
536 break;
538 case CIPHER_TKIP:
539 wsec = 2;
540 break;
542 case CIPHER_CCMP:
543 wsec = 4;
544 break;
546 default:
547 wsec = 0;
548 break;
551 switch (params->key_mgmt_suite) {
552 case KEY_MGMT_802_1X:
553 wpa_auth = 1;
554 break;
556 case KEY_MGMT_PSK:
557 wpa_auth = 2;
558 break;
560 default:
561 wpa_auth = 255;
562 break;
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 */
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;
580 return 0;
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,