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
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.
22 #include <sys/ioctl.h>
27 #include <netpacket/packet.h>
28 #include <net/ethernet.h> /* the L2 protocols */
30 #include <linux/if_packet.h>
31 #include <linux/if_ether.h> /* The L2 protocols */
36 /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
37 * WRT54G GPL tarball. */
43 struct wpa_driver_broadcom_data {
47 char ifname[IFNAMSIZ + 1];
51 #ifndef WLC_DEAUTHENTICATE
52 #define WLC_DEAUTHENTICATE 143
54 #ifndef WLC_DEAUTHENTICATE_WITH_REASON
55 #define WLC_DEAUTHENTICATE_WITH_REASON 201
57 #ifndef WLC_SET_TKIP_COUNTERMEASURES
58 #define WLC_SET_TKIP_COUNTERMEASURES 202
61 #if !defined(PSK_ENABLED) /* NEW driver interface */
62 #define WL_VERSION 360130
63 /* wireless authentication bit vector */
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;
83 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
86 static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
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); */
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",
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)
118 os_memset(bssid, 0, ETH_ALEN);
122 static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
124 struct wpa_driver_broadcom_data *drv = priv;
127 if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
130 os_memcpy(ssid, s.SSID, 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)) ==
143 broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1)
151 wsec &= ~(TKIP_ENABLED | AES_ENABLED);
154 if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) ==
156 broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1)
159 /* FIX: magic number / error handling? */
160 broadcom_ioctl(drv, 122, &ea, sizeof(ea));
165 static int wpa_driver_broadcom_set_key(const char *ifname, void *priv,
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)
171 struct wpa_driver_broadcom_data *drv = priv;
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);
183 wkt.algo = CRYPTO_ALGO_OFF;
186 wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
189 wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
192 wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
193 * AES_OCB_MSDU, AES_OCB_MPDU? */
196 wkt.algo = CRYPTO_ALGO_NALG;
200 if (seq && seq_len > 0)
201 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
204 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
208 if (key && key_len > 0) {
209 os_memcpy(wkt.data, key, key_len);
211 /* hack hack hack XXX */
212 os_memcpy(&wkt.data[16], &key[24], 8);
213 os_memcpy(&wkt.data[24], &key[16], 8);
216 /* wkt.algo = CRYPTO_ALGO_...; */
217 wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
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));
229 static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
234 wl_wpa_header_t *wwh;
235 union wpa_event_data data;
238 if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
241 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
243 if ((size_t) left < sizeof(wl_wpa_header_t))
246 wwh = (wl_wpa_header_t *) buf;
248 if (wwh->snap.type != WL_WPA_ETHER_TYPE)
250 if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
253 os_memset(&data, 0, sizeof(data));
257 left -= WL_WPA_HEADER_LEN;
258 wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
261 resp_ies = os_malloc(left);
262 if (resp_ies == NULL)
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;
269 wpa_supplicant_event(ctx, EVENT_ASSOC, &data);
272 case WLC_DISASSOC_MSG:
273 wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
274 wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
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);
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);
287 wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
293 static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
296 struct sockaddr_ll ll;
297 struct wpa_driver_broadcom_data *drv;
300 /* open socket to kernel */
301 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
306 os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
307 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
308 perror(ifr.ifr_name);
313 drv = os_zalloc(sizeof(*drv));
317 os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
320 s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
322 perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
323 close(drv->ioctl_sock);
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;
333 ll.sll_pkttype = PACKET_HOST;
336 if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
337 perror("bind(netlink)");
339 close(drv->ioctl_sock);
344 eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
347 wpa_driver_broadcom_set_wpa(drv, 1);
352 static void wpa_driver_broadcom_deinit(void *priv)
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);
363 static int wpa_driver_broadcom_set_countermeasures(void *priv,
367 struct wpa_driver_broadcom_data *drv = priv;
369 return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
376 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
378 struct wpa_driver_broadcom_data *drv = priv;
379 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
380 int _restrict = (enabled ? 1 : 0);
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)
391 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
394 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
395 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
398 static int wpa_driver_broadcom_scan(void *priv,
399 struct wpa_driver_scan_params *params)
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;
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)
414 eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
415 eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv,
421 static const int frequency_list[] = {
422 2412, 2417, 2422, 2427, 2432, 2437, 2442,
423 2447, 2452, 2457, 2462, 2467, 2472, 2484
432 } __attribute__ ((packed));
434 static struct wpa_scan_results *
435 wpa_driver_broadcom_get_scan_results(void *priv)
437 struct wpa_driver_broadcom_data *drv = priv;
439 wl_scan_results_t *wsr;
442 struct wpa_scan_results *res;
444 buf = os_malloc(WLC_IOCTL_MAXLEN);
448 wsr = (wl_scan_results_t *) buf;
450 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
454 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
459 res = os_zalloc(sizeof(*res));
465 res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *));
466 if (res->res == NULL) {
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);
477 res->res[res->num++] = r;
479 os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN);
480 r->freq = frequency_list[wbi->channel - 1];
482 os_memcpy(r + 1, wbi + 1, wbi->ie_length);
483 r->ie_len = wbi->ie_length;
485 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
488 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
490 wsr->buflen, (unsigned long) ap_num);
496 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
499 struct wpa_driver_broadcom_data *drv = priv;
501 wdt.val = reason_code;
502 os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
504 return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
508 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
511 struct wpa_driver_broadcom_data *drv = priv;
512 return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0);
516 wpa_driver_broadcom_associate(void *priv,
517 struct wpa_driver_associate_params *params)
519 struct wpa_driver_broadcom_data *drv = priv;
528 ret = wpa_driver_broadcom_set_drop_unencrypted(
529 drv, params->drop_unencrypted);
531 s.SSID_len = params->ssid_len;
532 os_memcpy(s.SSID, params->ssid, params->ssid_len);
534 switch (params->pairwise_suite) {
553 switch (params->key_mgmt_suite) {
554 case KEY_MGMT_802_1X:
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 */
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)
585 const struct wpa_driver_ops wpa_driver_broadcom_ops = {
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,