hostapd: Update vendor branch to 0.6.10
[dragonfly.git] / contrib / hostapd / src / drivers / driver_broadcom.c
blob3600dae1104683beda7f049a4aaabb690393e319
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>
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.
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.
20 #include "includes.h"
22 #include <sys/ioctl.h>
24 #include "common.h"
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>
36 /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
37 * WRT54G GPL tarball. */
38 #include <wlioctl.h>
40 #include "driver.h"
41 #include "eloop.h"
43 struct wpa_driver_broadcom_data {
44 void *ctx;
45 int ioctl_sock;
46 int event_sock;
47 char ifname[IFNAMSIZ + 1];
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
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
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))
71 #define WSEC_PRIMARY_KEY WL_PRIMARY_KEY
73 typedef wl_wsec_key_t wsec_key_t;
74 #endif
76 typedef struct {
77 uint32 val;
78 struct ether_addr ea;
79 uint16 res;
80 } wlc_deauth_t;
83 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
84 void *timeout_ctx);
86 static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
87 void *buf, int len)
89 struct ifreq ifr;
90 wl_ioctl_t ioc;
91 int ret = 0;
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); */
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);
109 return ret;
112 static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid)
114 struct wpa_driver_broadcom_data *drv = priv;
115 if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0)
116 return 0;
118 os_memset(bssid, 0, ETH_ALEN);
119 return -1;
122 static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
124 struct wpa_driver_broadcom_data *drv = priv;
125 wlc_ssid_t s;
127 if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
128 return -1;
130 os_memcpy(ssid, s.SSID, s.SSID_len);
131 return s.SSID_len;
134 static int wpa_driver_broadcom_set_wpa(void *priv, int enable)
136 struct wpa_driver_broadcom_data *drv = priv;
137 unsigned int wauth, wsec;
138 struct ether_addr ea;
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;
146 if (enable) {
147 wauth = PSK_ENABLED;
148 wsec = TKIP_ENABLED;
149 } else {
150 wauth = 255;
151 wsec &= ~(TKIP_ENABLED | AES_ENABLED);
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;
159 /* FIX: magic number / error handling? */
160 broadcom_ioctl(drv, 122, &ea, sizeof(ea));
162 return 0;
165 static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg,
166 const u8 *addr, int key_idx, int set_tx,
167 const u8 *seq, size_t seq_len,
168 const u8 *key, size_t key_len)
170 struct wpa_driver_broadcom_data *drv = priv;
171 int ret;
172 wsec_key_t wkt;
174 os_memset(&wkt, 0, sizeof wkt);
175 wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d",
176 set_tx ? "PRIMARY " : "", key_idx, alg);
177 if (key && key_len > 0)
178 wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len);
180 switch (alg) {
181 case WPA_ALG_NONE:
182 wkt.algo = CRYPTO_ALGO_OFF;
183 break;
184 case WPA_ALG_WEP:
185 wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
186 break;
187 case WPA_ALG_TKIP:
188 wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
189 break;
190 case WPA_ALG_CCMP:
191 wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
192 * AES_OCB_MSDU, AES_OCB_MPDU? */
193 break;
194 default:
195 wkt.algo = CRYPTO_ALGO_NALG;
196 break;
199 if (seq && seq_len > 0)
200 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
202 if (addr)
203 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
205 wkt.index = key_idx;
206 wkt.len = key_len;
207 if (key && key_len > 0) {
208 os_memcpy(wkt.data, key, key_len);
209 if (key_len == 32) {
210 /* hack hack hack XXX */
211 os_memcpy(&wkt.data[16], &key[24], 8);
212 os_memcpy(&wkt.data[24], &key[16], 8);
215 /* wkt.algo = CRYPTO_ALGO_...; */
216 wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
217 if (addr && set_tx)
218 os_memcpy(&wkt.ea, addr, sizeof(wkt.ea));
219 ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt));
220 if (addr && set_tx) {
221 /* FIX: magic number / error handling? */
222 broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea));
224 return ret;
228 static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
229 void *sock_ctx)
231 char buf[8192];
232 int left;
233 wl_wpa_header_t *wwh;
234 union wpa_event_data data;
236 if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
237 return;
239 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
241 if ((size_t) left < sizeof(wl_wpa_header_t))
242 return;
244 wwh = (wl_wpa_header_t *) buf;
246 if (wwh->snap.type != WL_WPA_ETHER_TYPE)
247 return;
248 if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
249 return;
251 os_memset(&data, 0, sizeof(data));
253 switch (wwh->type) {
254 case WLC_ASSOC_MSG:
255 left -= WL_WPA_HEADER_LEN;
256 wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
257 left);
258 if (left > 0) {
259 data.assoc_info.resp_ies = os_malloc(left);
260 if (data.assoc_info.resp_ies == NULL)
261 return;
262 os_memcpy(data.assoc_info.resp_ies,
263 buf + WL_WPA_HEADER_LEN, left);
264 data.assoc_info.resp_ies_len = left;
265 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes "
266 "into resp_ies",
267 data.assoc_info.resp_ies, left);
269 /* data.assoc_info.req_ies = NULL; */
270 /* data.assoc_info.req_ies_len = 0; */
272 wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
273 wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
274 break;
275 case WLC_DISASSOC_MSG:
276 wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
277 wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
278 break;
279 case WLC_PTK_MIC_MSG:
280 wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE");
281 data.michael_mic_failure.unicast = 1;
282 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
283 break;
284 case WLC_GTK_MIC_MSG:
285 wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE");
286 data.michael_mic_failure.unicast = 0;
287 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
288 break;
289 default:
290 wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
291 wwh->type);
292 break;
294 os_free(data.assoc_info.resp_ies);
297 static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
299 int s;
300 struct sockaddr_ll ll;
301 struct wpa_driver_broadcom_data *drv;
302 struct ifreq ifr;
304 /* open socket to kernel */
305 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
306 perror("socket");
307 return NULL;
309 /* do it */
310 os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
311 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
312 perror(ifr.ifr_name);
313 return NULL;
317 drv = os_zalloc(sizeof(*drv));
318 if (drv == NULL)
319 return NULL;
320 drv->ctx = ctx;
321 os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
322 drv->ioctl_sock = s;
324 s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
325 if (s < 0) {
326 perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
327 close(drv->ioctl_sock);
328 os_free(drv);
329 return NULL;
332 os_memset(&ll, 0, sizeof(ll));
333 ll.sll_family = AF_PACKET;
334 ll.sll_protocol = ntohs(ETH_P_802_2);
335 ll.sll_ifindex = ifr.ifr_ifindex;
336 ll.sll_hatype = 0;
337 ll.sll_pkttype = PACKET_HOST;
338 ll.sll_halen = 0;
340 if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
341 perror("bind(netlink)");
342 close(s);
343 close(drv->ioctl_sock);
344 os_free(drv);
345 return NULL;
348 eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
349 NULL);
350 drv->event_sock = s;
352 return drv;
355 static void wpa_driver_broadcom_deinit(void *priv)
357 struct wpa_driver_broadcom_data *drv = priv;
358 eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
359 eloop_unregister_read_sock(drv->event_sock);
360 close(drv->event_sock);
361 close(drv->ioctl_sock);
362 os_free(drv);
365 static int wpa_driver_broadcom_set_countermeasures(void *priv,
366 int enabled)
368 #if 0
369 struct wpa_driver_broadcom_data *drv = priv;
370 /* FIX: ? */
371 return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
372 sizeof(enabled));
373 #else
374 return 0;
375 #endif
378 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
380 struct wpa_driver_broadcom_data *drv = priv;
381 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
382 int restrict = (enabled ? 1 : 0);
384 if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT,
385 &restrict, sizeof(restrict)) < 0 ||
386 broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT,
387 &restrict, sizeof(restrict)) < 0)
388 return -1;
390 return 0;
393 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
394 void *timeout_ctx)
396 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
397 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
400 static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid,
401 size_t ssid_len)
403 struct wpa_driver_broadcom_data *drv = priv;
404 wlc_ssid_t wst = { 0, "" };
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);
411 if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0)
412 return -1;
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;
421 static const int frequency_list[] = {
422 2412, 2417, 2422, 2427, 2432, 2437, 2442,
423 2447, 2452, 2457, 2462, 2467, 2472, 2484
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));
434 static int
435 wpa_driver_broadcom_get_scan_results(void *priv,
436 struct wpa_scan_result *results,
437 size_t max_size)
439 struct wpa_driver_broadcom_data *drv = priv;
440 char *buf;
441 wl_scan_results_t *wsr;
442 wl_bss_info_t *wbi;
443 size_t ap_num;
445 buf = os_malloc(WLC_IOCTL_MAXLEN);
446 if (buf == NULL)
447 return -1;
449 wsr = (wl_scan_results_t *) buf;
451 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
452 wsr->version = 107;
453 wsr->count = 0;
455 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
456 os_free(buf);
457 return -1;
460 os_memset(results, 0, max_size * sizeof(struct wpa_scan_result));
462 for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
463 int left;
464 struct bss_ie_hdr *ie;
466 os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN);
467 os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len);
468 results[ap_num].ssid_len = wbi->SSID_len;
469 results[ap_num].freq = frequency_list[wbi->channel - 1];
470 /* get ie's */
471 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs",
472 (u8 *) wbi + sizeof(*wbi), wbi->ie_length);
473 ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi));
474 for (left = wbi->ie_length; left > 0;
475 left -= (ie->len + 2), ie = (struct bss_ie_hdr *)
476 ((u8 *) ie + 2 + ie->len)) {
477 wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d",
478 ie->elem_id, ie->len);
479 if (ie->len >= 3)
480 wpa_printf(MSG_MSGDUMP,
481 "BROADCOM: oui:%02x%02x%02x",
482 ie->oui[0], ie->oui[1], ie->oui[2]);
483 if (ie->elem_id != 0xdd ||
484 ie->len < 6 ||
485 os_memcmp(ie->oui, WPA_OUI, 3) != 0)
486 continue;
487 os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2);
488 results[ap_num].wpa_ie_len = ie->len + 2;
489 break;
492 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
495 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
496 "BSSes)",
497 wsr->buflen, (unsigned long) ap_num);
499 os_free(buf);
500 return ap_num;
503 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
504 int reason_code)
506 struct wpa_driver_broadcom_data *drv = priv;
507 wlc_deauth_t wdt;
508 wdt.val = reason_code;
509 os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
510 wdt.res = 0x7fff;
511 return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
512 sizeof(wdt));
515 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
516 int reason_code)
518 struct wpa_driver_broadcom_data *drv = priv;
519 return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0);
522 static int
523 wpa_driver_broadcom_associate(void *priv,
524 struct wpa_driver_associate_params *params)
526 struct wpa_driver_broadcom_data *drv = priv;
527 wlc_ssid_t s;
528 int infra = 1;
529 int auth = 0;
530 int wsec = 4;
531 int dummy;
532 int wpa_auth;
534 s.SSID_len = params->ssid_len;
535 os_memcpy(s.SSID, params->ssid, params->ssid_len);
537 switch (params->pairwise_suite) {
538 case CIPHER_WEP40:
539 case CIPHER_WEP104:
540 wsec = 1;
541 break;
543 case CIPHER_TKIP:
544 wsec = 2;
545 break;
547 case CIPHER_CCMP:
548 wsec = 4;
549 break;
551 default:
552 wsec = 0;
553 break;
556 switch (params->key_mgmt_suite) {
557 case KEY_MGMT_802_1X:
558 wpa_auth = 1;
559 break;
561 case KEY_MGMT_PSK:
562 wpa_auth = 2;
563 break;
565 default:
566 wpa_auth = 255;
567 break;
570 /* printf("broadcom_associate: %u %u %u\n", pairwise_suite,
571 * group_suite, key_mgmt_suite);
572 * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec));
573 * wl join uses wlc_sec_wep here, not wlc_set_wsec */
575 if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 ||
576 broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth,
577 sizeof(wpa_auth)) < 0 ||
578 broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 ||
579 broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 ||
580 broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 ||
581 broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 ||
582 broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0)
583 return -1;
585 return 0;
588 const struct wpa_driver_ops wpa_driver_broadcom_ops = {
589 .name = "broadcom",
590 .desc = "Broadcom wl.o driver",
591 .get_bssid = wpa_driver_broadcom_get_bssid,
592 .get_ssid = wpa_driver_broadcom_get_ssid,
593 .set_wpa = wpa_driver_broadcom_set_wpa,
594 .set_key = wpa_driver_broadcom_set_key,
595 .init = wpa_driver_broadcom_init,
596 .deinit = wpa_driver_broadcom_deinit,
597 .set_countermeasures = wpa_driver_broadcom_set_countermeasures,
598 .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted,
599 .scan = wpa_driver_broadcom_scan,
600 .get_scan_results = wpa_driver_broadcom_get_scan_results,
601 .deauthenticate = wpa_driver_broadcom_deauthenticate,
602 .disassociate = wpa_driver_broadcom_disassociate,
603 .associate = wpa_driver_broadcom_associate,