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 43struct 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 73typedef wl_wsec_key_t wsec_key_t; 74#endif 75 76typedef struct { 77 uint32 val; 78 struct ether_addr ea; 79 uint16 res; 80} wlc_deauth_t; 81 82 83static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 84 void *timeout_ctx); 85 86static 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 112static 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 122static 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 134static 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 165static 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) 169{ 170 struct wpa_driver_broadcom_data *drv = priv; 171 int ret; 172 wsec_key_t wkt; 173 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); 179 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; 197 } 198 199 if (seq && seq_len > 0) 200 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); 201 202 if (addr) 203 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); 204 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); 213 } 214 } 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)); 223 } 224 return ret; 225} 226 227 228static void wpa_driver_broadcom_event_receive(int sock, void *ctx, 229 void *sock_ctx) 230{ 231 char buf[8192]; 232 int left; 233 wl_wpa_header_t *wwh; 234 union wpa_event_data data; 235 236 if ((left = recv(sock, buf, sizeof buf, 0)) < 0) 237 return; 238 239 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); 240 241 if ((size_t) left < sizeof(wl_wpa_header_t)) 242 return; 243 244 wwh = (wl_wpa_header_t *) buf; 245 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; 250 251 os_memset(&data, 0, sizeof(data)); 252 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); 268 } 269 /* data.assoc_info.req_ies = NULL; */ 270 /* data.assoc_info.req_ies_len = 0; */ 271 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; 293 } 294 os_free(data.assoc_info.resp_ies); 295} 296 297static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) 298{ 299 int s; 300 struct sockaddr_ll ll; 301 struct wpa_driver_broadcom_data *drv; 302 struct ifreq ifr; 303 304 /* open socket to kernel */ 305 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 306 perror("socket"); 307 return NULL; 308 } 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; 314 } 315 316 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; 323 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; 330 } 331 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; 339 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; 346 } 347 348 eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, 349 NULL); 350 drv->event_sock = s; 351 352 return drv; 353} 354 355static void wpa_driver_broadcom_deinit(void *priv) 356{ 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); 363} 364 365static int wpa_driver_broadcom_set_countermeasures(void *priv, 366 int enabled) 367{ 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 376} 377 378static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) 379{ 380 struct wpa_driver_broadcom_data *drv = priv; 381 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ 382 int restrict = (enabled ? 1 : 0); 383 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; 389 390 return 0; 391} 392 393static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 394 void *timeout_ctx) 395{ 396 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); 397 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); 398} 399 400static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, 401 size_t ssid_len) 402{ 403 struct wpa_driver_broadcom_data *drv = priv; 404 wlc_ssid_t wst = { 0, "" }; 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 421static const int frequency_list[] = { 422 2412, 2417, 2422, 2427, 2432, 2437, 2442, 423 2447, 2452, 2457, 2462, 2467, 2472, 2484 424}; 425 426struct 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 434static int 435wpa_driver_broadcom_get_scan_results(void *priv, 436 struct wpa_scan_result *results, 437 size_t max_size) 438{ 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; 444 445 buf = os_malloc(WLC_IOCTL_MAXLEN); 446 if (buf == NULL) 447 return -1; 448 449 wsr = (wl_scan_results_t *) buf; 450 451 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); 452 wsr->version = 107; 453 wsr->count = 0; 454 455 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { 456 os_free(buf); 457 return -1; 458 } 459 460 os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); 461 462 for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { 463 int left; 464 struct bss_ie_hdr *ie; 465 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; 490 } 491 492 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); 493 } 494 495 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " 496 "BSSes)", 497 wsr->buflen, (unsigned long) ap_num); 498 499 os_free(buf); 500 return ap_num; 501} 502 503static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, 504 int reason_code) 505{ 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)); 513} 514 515static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, 516 int reason_code) 517{ 518 struct wpa_driver_broadcom_data *drv = priv; 519 return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); 520} 521 522static int 523wpa_driver_broadcom_associate(void *priv, 524 struct wpa_driver_associate_params *params) 525{ 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; 533 534 s.SSID_len = params->ssid_len; 535 os_memcpy(s.SSID, params->ssid, params->ssid_len); 536 537 switch (params->pairwise_suite) { 538 case CIPHER_WEP40: 539 case CIPHER_WEP104: 540 wsec = 1; 541 break; 542 543 case CIPHER_TKIP: 544 wsec = 2; 545 break; 546 547 case CIPHER_CCMP: 548 wsec = 4; 549 break; 550 551 default: 552 wsec = 0; 553 break; 554 } 555 556 switch (params->key_mgmt_suite) { 557 case KEY_MGMT_802_1X: 558 wpa_auth = 1; 559 break; 560 561 case KEY_MGMT_PSK: 562 wpa_auth = 2; 563 break; 564 565 default: 566 wpa_auth = 255; 567 break; 568 } 569 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 */ 574 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; 584 585 return 0; 586} 587 588const 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, 604}; 605