1 /* 2 * WPA Supplicant - driver interaction with old Broadcom wl.o driver 3 * Copyright (c) 2004, Nikki Chumkov <nikki (at) gattaca.ru> 4 * Copyright (c) 2004, Jouni Malinen <j (at) 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 43 struct 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 73 typedef wl_wsec_key_t wsec_key_t; 74 #endif 75 76 typedef struct { 77 uint32 val; 78 struct ether_addr ea; 79 uint16 res; 80 } wlc_deauth_t; 81 82 83 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 84 void *timeout_ctx); 85 86 static 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 112 static 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 122 static 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 134 static 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 165 static int wpa_driver_broadcom_set_key(const char *ifname, void *priv, 166 enum wpa_alg alg, 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) 170 { 171 struct wpa_driver_broadcom_data *drv = priv; 172 int ret; 173 wsec_key_t wkt; 174 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); 180 181 switch (alg) { 182 case WPA_ALG_NONE: 183 wkt.algo = CRYPTO_ALGO_OFF; 184 break; 185 case WPA_ALG_WEP: 186 wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ 187 break; 188 case WPA_ALG_TKIP: 189 wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ 190 break; 191 case WPA_ALG_CCMP: 192 wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; 193 * AES_OCB_MSDU, AES_OCB_MPDU? */ 194 break; 195 default: 196 wkt.algo = CRYPTO_ALGO_NALG; 197 break; 198 } 199 200 if (seq && seq_len > 0) 201 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); 202 203 if (addr) 204 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); 205 206 wkt.index = key_idx; 207 wkt.len = key_len; 208 if (key && key_len > 0) { 209 os_memcpy(wkt.data, key, key_len); 210 if (key_len == 32) { 211 /* hack hack hack XXX */ 212 os_memcpy(&wkt.data[16], &key[24], 8); 213 os_memcpy(&wkt.data[24], &key[16], 8); 214 } 215 } 216 /* wkt.algo = CRYPTO_ALGO_...; */ 217 wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; 218 if (addr && set_tx) 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)); 224 } 225 return ret; 226 } 227 228 229 static void wpa_driver_broadcom_event_receive(int sock, void *ctx, 230 void *sock_ctx) 231 { 232 char buf[8192]; 233 int left; 234 wl_wpa_header_t *wwh; 235 union wpa_event_data data; 236 u8 *resp_ies = NULL; 237 238 if ((left = recv(sock, buf, sizeof buf, 0)) < 0) 239 return; 240 241 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); 242 243 if ((size_t) left < sizeof(wl_wpa_header_t)) 244 return; 245 246 wwh = (wl_wpa_header_t *) buf; 247 248 if (wwh->snap.type != WL_WPA_ETHER_TYPE) 249 return; 250 if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) 251 return; 252 253 os_memset(&data, 0, sizeof(data)); 254 255 switch (wwh->type) { 256 case WLC_ASSOC_MSG: 257 left -= WL_WPA_HEADER_LEN; 258 wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", 259 left); 260 if (left > 0) { 261 resp_ies = os_malloc(left); 262 if (resp_ies == NULL) 263 return; 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; 267 } 268 269 wpa_supplicant_event(ctx, EVENT_ASSOC, &data); 270 os_free(resp_ies); 271 break; 272 case WLC_DISASSOC_MSG: 273 wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); 274 wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); 275 break; 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); 280 break; 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); 285 break; 286 default: 287 wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", 288 wwh->type); 289 break; 290 } 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 wpa_driver_broadcom_set_wpa(drv, 1); 348 349 return drv; 350 } 351 352 static void wpa_driver_broadcom_deinit(void *priv) 353 { 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); 360 os_free(drv); 361 } 362 363 static int wpa_driver_broadcom_set_countermeasures(void *priv, 364 int enabled) 365 { 366 #if 0 367 struct wpa_driver_broadcom_data *drv = priv; 368 /* FIX: ? */ 369 return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, 370 sizeof(enabled)); 371 #else 372 return 0; 373 #endif 374 } 375 376 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) 377 { 378 struct wpa_driver_broadcom_data *drv = priv; 379 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ 380 int _restrict = (enabled ? 1 : 0); 381 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) 386 return -1; 387 388 return 0; 389 } 390 391 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 392 void *timeout_ctx) 393 { 394 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); 395 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); 396 } 397 398 static int wpa_driver_broadcom_scan(void *priv, 399 struct wpa_driver_scan_params *params) 400 { 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; 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 421 static const int frequency_list[] = { 422 2412, 2417, 2422, 2427, 2432, 2437, 2442, 423 2447, 2452, 2457, 2462, 2467, 2472, 2484 424 }; 425 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)); 433 434 static struct wpa_scan_results * 435 wpa_driver_broadcom_get_scan_results(void *priv) 436 { 437 struct wpa_driver_broadcom_data *drv = priv; 438 char *buf; 439 wl_scan_results_t *wsr; 440 wl_bss_info_t *wbi; 441 size_t ap_num; 442 struct wpa_scan_results *res; 443 444 buf = os_malloc(WLC_IOCTL_MAXLEN); 445 if (buf == NULL) 446 return NULL; 447 448 wsr = (wl_scan_results_t *) buf; 449 450 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); 451 wsr->version = 107; 452 wsr->count = 0; 453 454 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { 455 os_free(buf); 456 return NULL; 457 } 458 459 res = os_zalloc(sizeof(*res)); 460 if (res == NULL) { 461 os_free(buf); 462 return NULL; 463 } 464 465 res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *)); 466 if (res->res == NULL) { 467 os_free(res); 468 os_free(buf); 469 return NULL; 470 } 471 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); 475 if (r == NULL) 476 break; 477 res->res[res->num++] = r; 478 479 os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN); 480 r->freq = frequency_list[wbi->channel - 1]; 481 /* get ie's */ 482 os_memcpy(r + 1, wbi + 1, wbi->ie_length); 483 r->ie_len = wbi->ie_length; 484 485 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); 486 } 487 488 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " 489 "BSSes)", 490 wsr->buflen, (unsigned long) ap_num); 491 492 os_free(buf); 493 return res; 494 } 495 496 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, 497 int reason_code) 498 { 499 struct wpa_driver_broadcom_data *drv = priv; 500 wlc_deauth_t wdt; 501 wdt.val = reason_code; 502 os_memcpy(&wdt.ea, addr, sizeof wdt.ea); 503 wdt.res = 0x7fff; 504 return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, 505 sizeof(wdt)); 506 } 507 508 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, 509 int reason_code) 510 { 511 struct wpa_driver_broadcom_data *drv = priv; 512 return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0); 513 } 514 515 static int 516 wpa_driver_broadcom_associate(void *priv, 517 struct wpa_driver_associate_params *params) 518 { 519 struct wpa_driver_broadcom_data *drv = priv; 520 wlc_ssid_t s; 521 int infra = 1; 522 int auth = 0; 523 int wsec = 4; 524 int dummy; 525 int wpa_auth; 526 int ret; 527 528 ret = wpa_driver_broadcom_set_drop_unencrypted( 529 drv, params->drop_unencrypted); 530 531 s.SSID_len = params->ssid_len; 532 os_memcpy(s.SSID, params->ssid, params->ssid_len); 533 534 switch (params->pairwise_suite) { 535 case CIPHER_WEP40: 536 case CIPHER_WEP104: 537 wsec = 1; 538 break; 539 540 case CIPHER_TKIP: 541 wsec = 2; 542 break; 543 544 case CIPHER_CCMP: 545 wsec = 4; 546 break; 547 548 default: 549 wsec = 0; 550 break; 551 } 552 553 switch (params->key_mgmt_suite) { 554 case KEY_MGMT_802_1X: 555 wpa_auth = 1; 556 break; 557 558 case KEY_MGMT_PSK: 559 wpa_auth = 2; 560 break; 561 562 default: 563 wpa_auth = 255; 564 break; 565 } 566 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 */ 571 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) 580 return -1; 581 582 return ret; 583 } 584 585 const struct wpa_driver_ops wpa_driver_broadcom_ops = { 586 .name = "broadcom", 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, 599 }; 600