1 /* 2 * WPA Supplicant - driver interaction with 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 16 #include "includes.h" 17 #include <sys/ioctl.h> 18 #if 0 19 #include <netpacket/packet.h> 20 #include <net/ethernet.h> /* the L2 protocols */ 21 #else 22 #include <linux/if_packet.h> 23 #include <linux/if_ether.h> /* The L2 protocols */ 24 #endif 25 #include <net/if.h> 26 #include <typedefs.h> 27 28 /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys 29 * WRT54G GPL tarball. */ 30 #include <wlioctl.h> 31 32 #include "common.h" 33 #include "driver.h" 34 #include "eloop.h" 35 #include "wpa_supplicant.h" 36 #include "wpa.h" 37 38 struct wpa_driver_broadcom_data { 39 void *ctx; 40 int ioctl_sock; 41 int event_sock; 42 char ifname[IFNAMSIZ + 1]; 43 }; 44 45 46 #ifndef WLC_DEAUTHENTICATE 47 #define WLC_DEAUTHENTICATE 143 48 #endif 49 #ifndef WLC_DEAUTHENTICATE_WITH_REASON 50 #define WLC_DEAUTHENTICATE_WITH_REASON 201 51 #endif 52 #ifndef WLC_SET_TKIP_COUNTERMEASURES 53 #define WLC_SET_TKIP_COUNTERMEASURES 202 54 #endif 55 56 #if !defined(PSK_ENABLED) /* NEW driver interface */ 57 #define WL_VERSION 360130 58 /* wireless authentication bit vector */ 59 #define WPA_ENABLED 1 60 #define PSK_ENABLED 2 61 62 #define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) 63 #define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) 64 #define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) 65 66 #define WSEC_PRIMARY_KEY WL_PRIMARY_KEY 67 68 typedef wl_wsec_key_t wsec_key_t; 69 #endif 70 71 typedef struct { 72 uint32 val; 73 struct ether_addr ea; 74 uint16 res; 75 } wlc_deauth_t; 76 77 78 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 79 void *timeout_ctx); 80 81 static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, 82 void *buf, int len) 83 { 84 struct ifreq ifr; 85 wl_ioctl_t ioc; 86 int ret = 0; 87 88 wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", 89 drv->ifname, cmd, len, buf); 90 /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ 91 92 ioc.cmd = cmd; 93 ioc.buf = buf; 94 ioc.len = len; 95 os_strncpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); 96 ifr.ifr_data = (caddr_t) &ioc; 97 if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { 98 if (cmd != WLC_GET_MAGIC) 99 perror(ifr.ifr_name); 100 wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", 101 cmd, ret); 102 } 103 104 return ret; 105 } 106 107 static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) 108 { 109 struct wpa_driver_broadcom_data *drv = priv; 110 if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) 111 return 0; 112 113 os_memset(bssid, 0, ETH_ALEN); 114 return -1; 115 } 116 117 static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) 118 { 119 struct wpa_driver_broadcom_data *drv = priv; 120 wlc_ssid_t s; 121 122 if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) 123 return -1; 124 125 os_memcpy(ssid, s.SSID, s.SSID_len); 126 return s.SSID_len; 127 } 128 129 static int wpa_driver_broadcom_set_wpa(void *priv, int enable) 130 { 131 struct wpa_driver_broadcom_data *drv = priv; 132 unsigned int wauth, wsec; 133 struct ether_addr ea; 134 135 os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); 136 if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == 137 -1 || 138 broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) 139 return -1; 140 141 if (enable) { 142 wauth = PSK_ENABLED; 143 wsec = TKIP_ENABLED; 144 } else { 145 wauth = 255; 146 wsec &= ~(TKIP_ENABLED | AES_ENABLED); 147 } 148 149 if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == 150 -1 || 151 broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) 152 return -1; 153 154 /* FIX: magic number / error handling? */ 155 broadcom_ioctl(drv, 122, &ea, sizeof(ea)); 156 157 return 0; 158 } 159 160 static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg, 161 const u8 *addr, int key_idx, int set_tx, 162 const u8 *seq, size_t seq_len, 163 const u8 *key, size_t key_len) 164 { 165 struct wpa_driver_broadcom_data *drv = priv; 166 int ret; 167 wsec_key_t wkt; 168 169 os_memset(&wkt, 0, sizeof wkt); 170 wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", 171 set_tx ? "PRIMARY " : "", key_idx, alg); 172 if (key && key_len > 0) 173 wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); 174 175 switch (alg) { 176 case WPA_ALG_NONE: 177 wkt.algo = CRYPTO_ALGO_OFF; 178 break; 179 case WPA_ALG_WEP: 180 wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ 181 break; 182 case WPA_ALG_TKIP: 183 wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ 184 break; 185 case WPA_ALG_CCMP: 186 wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; 187 * AES_OCB_MSDU, AES_OCB_MPDU? */ 188 break; 189 default: 190 wkt.algo = CRYPTO_ALGO_NALG; 191 break; 192 } 193 194 if (seq && seq_len > 0) 195 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); 196 197 if (addr) 198 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); 199 200 wkt.index = key_idx; 201 wkt.len = key_len; 202 if (key && key_len > 0) { 203 os_memcpy(wkt.data, key, key_len); 204 if (key_len == 32) { 205 /* hack hack hack XXX */ 206 os_memcpy(&wkt.data[16], &key[24], 8); 207 os_memcpy(&wkt.data[24], &key[16], 8); 208 } 209 } 210 /* wkt.algo = CRYPTO_ALGO_...; */ 211 wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; 212 if (addr && set_tx) 213 os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); 214 ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); 215 if (addr && set_tx) { 216 /* FIX: magic number / error handling? */ 217 broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); 218 } 219 return ret; 220 } 221 222 223 static void wpa_driver_broadcom_event_receive(int sock, void *ctx, 224 void *sock_ctx) 225 { 226 char buf[8192]; 227 int left; 228 wl_wpa_header_t *wwh; 229 union wpa_event_data data; 230 231 if ((left = recv(sock, buf, sizeof buf, 0)) < 0) 232 return; 233 234 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", buf, left); 235 236 if ((size_t) left < sizeof(wl_wpa_header_t)) 237 return; 238 239 wwh = (wl_wpa_header_t *) buf; 240 241 if (wwh->snap.type != WL_WPA_ETHER_TYPE) 242 return; 243 if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) 244 return; 245 246 os_memset(&data, 0, sizeof(data)); 247 248 switch (wwh->type) { 249 case WLC_ASSOC_MSG: 250 left -= WL_WPA_HEADER_LEN; 251 wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", 252 left); 253 if (left > 0) { 254 data.assoc_info.resp_ies = os_malloc(left); 255 if (data.assoc_info.resp_ies == NULL) 256 return; 257 os_memcpy(data.assoc_info.resp_ies, 258 buf + WL_WPA_HEADER_LEN, left); 259 data.assoc_info.resp_ies_len = left; 260 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " 261 "into resp_ies", 262 data.assoc_info.resp_ies, left); 263 } 264 /* data.assoc_info.req_ies = NULL; */ 265 /* data.assoc_info.req_ies_len = 0; */ 266 267 wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); 268 wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); 269 break; 270 case WLC_DISASSOC_MSG: 271 wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); 272 wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); 273 break; 274 case WLC_PTK_MIC_MSG: 275 wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); 276 data.michael_mic_failure.unicast = 1; 277 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); 278 break; 279 case WLC_GTK_MIC_MSG: 280 wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); 281 data.michael_mic_failure.unicast = 0; 282 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); 283 break; 284 default: 285 wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", 286 wwh->type); 287 break; 288 } 289 os_free(data.assoc_info.resp_ies); 290 } 291 292 static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) 293 { 294 int s; 295 struct sockaddr_ll ll; 296 struct wpa_driver_broadcom_data *drv; 297 struct ifreq ifr; 298 299 /* open socket to kernel */ 300 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 301 perror("socket"); 302 return NULL; 303 } 304 /* do it */ 305 os_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 306 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { 307 perror(ifr.ifr_name); 308 return NULL; 309 } 310 311 312 drv = os_zalloc(sizeof(*drv)); 313 if (drv == NULL) 314 return NULL; 315 drv->ctx = ctx; 316 os_strncpy(drv->ifname, ifname, sizeof(drv->ifname)); 317 drv->ioctl_sock = s; 318 319 s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); 320 if (s < 0) { 321 perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); 322 close(drv->ioctl_sock); 323 os_free(drv); 324 return NULL; 325 } 326 327 os_memset(&ll, 0, sizeof(ll)); 328 ll.sll_family = AF_PACKET; 329 ll.sll_protocol = ntohs(ETH_P_802_2); 330 ll.sll_ifindex = ifr.ifr_ifindex; 331 ll.sll_hatype = 0; 332 ll.sll_pkttype = PACKET_HOST; 333 ll.sll_halen = 0; 334 335 if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { 336 perror("bind(netlink)"); 337 close(s); 338 close(drv->ioctl_sock); 339 os_free(drv); 340 return NULL; 341 } 342 343 eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, 344 NULL); 345 drv->event_sock = s; 346 347 return drv; 348 } 349 350 static void wpa_driver_broadcom_deinit(void *priv) 351 { 352 struct wpa_driver_broadcom_data *drv = priv; 353 eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); 354 eloop_unregister_read_sock(drv->event_sock); 355 close(drv->event_sock); 356 close(drv->ioctl_sock); 357 os_free(drv); 358 } 359 360 static int wpa_driver_broadcom_set_countermeasures(void *priv, 361 int enabled) 362 { 363 #if 0 364 struct wpa_driver_broadcom_data *drv = priv; 365 /* FIX: ? */ 366 return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, 367 sizeof(enabled)); 368 #else 369 return 0; 370 #endif 371 } 372 373 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) 374 { 375 struct wpa_driver_broadcom_data *drv = priv; 376 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ 377 int restrict = (enabled ? 1 : 0); 378 379 if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, 380 &restrict, sizeof(restrict)) < 0 || 381 broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, 382 &restrict, sizeof(restrict)) < 0) 383 return -1; 384 385 return 0; 386 } 387 388 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 389 void *timeout_ctx) 390 { 391 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); 392 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); 393 } 394 395 static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, 396 size_t ssid_len) 397 { 398 struct wpa_driver_broadcom_data *drv = priv; 399 wlc_ssid_t wst = { 0, "" }; 400 401 if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { 402 wst.SSID_len = ssid_len; 403 os_memcpy(wst.SSID, ssid, ssid_len); 404 } 405 406 if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) 407 return -1; 408 409 eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); 410 eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, 411 drv->ctx); 412 return 0; 413 } 414 415 416 static const int frequency_list[] = { 417 2412, 2417, 2422, 2427, 2432, 2437, 2442, 418 2447, 2452, 2457, 2462, 2467, 2472, 2484 419 }; 420 421 struct bss_ie_hdr { 422 u8 elem_id; 423 u8 len; 424 u8 oui[3]; 425 /* u8 oui_type; */ 426 /* u16 version; */ 427 } __attribute__ ((packed)); 428 429 static int 430 wpa_driver_broadcom_get_scan_results(void *priv, 431 struct wpa_scan_result *results, 432 size_t max_size) 433 { 434 struct wpa_driver_broadcom_data *drv = priv; 435 char *buf; 436 wl_scan_results_t *wsr = (wl_scan_results_t *) buf; 437 wl_bss_info_t *wbi; 438 size_t ap_num; 439 440 buf = os_malloc(WLC_IOCTL_MAXLEN); 441 if (buf == NULL) 442 return -1; 443 444 wsr = (wl_scan_results_t *) buf; 445 446 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); 447 wsr->version = 107; 448 wsr->count = 0; 449 450 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { 451 os_free(buf); 452 return -1; 453 } 454 455 os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); 456 457 for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { 458 int left; 459 struct bss_ie_hdr *ie; 460 461 os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); 462 os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); 463 results[ap_num].ssid_len = wbi->SSID_len; 464 results[ap_num].freq = frequency_list[wbi->channel - 1]; 465 /* get ie's */ 466 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", 467 (u8 *) wbi + sizeof(*wbi), wbi->ie_length); 468 ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); 469 for (left = wbi->ie_length; left > 0; 470 left -= (ie->len + 2), ie = (struct bss_ie_hdr *) 471 ((u8 *) ie + 2 + ie->len)) { 472 wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", 473 ie->elem_id, ie->len); 474 if (ie->len >= 3) 475 wpa_printf(MSG_MSGDUMP, 476 "BROADCOM: oui:%02x%02x%02x", 477 ie->oui[0], ie->oui[1], ie->oui[2]); 478 if (ie->elem_id != 0xdd || 479 ie->len < 6 || 480 os_memcmp(ie->oui, WPA_OUI, 3) != 0) 481 continue; 482 os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); 483 results[ap_num].wpa_ie_len = ie->len + 2; 484 break; 485 } 486 487 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); 488 } 489 490 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " 491 "BSSes)", 492 wsr->buflen, (unsigned long) ap_num); 493 494 os_free(buf); 495 return ap_num; 496 } 497 498 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, 499 int reason_code) 500 { 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)); 508 } 509 510 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, 511 int reason_code) 512 { 513 struct wpa_driver_broadcom_data *drv = priv; 514 return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); 515 } 516 517 static int 518 wpa_driver_broadcom_associate(void *priv, 519 struct wpa_driver_associate_params *params) 520 { 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; 528 529 s.SSID_len = params->ssid_len; 530 os_memcpy(s.SSID, params->ssid, params->ssid_len); 531 532 switch (params->pairwise_suite) { 533 case CIPHER_WEP40: 534 case CIPHER_WEP104: 535 wsec = 1; 536 break; 537 538 case CIPHER_TKIP: 539 wsec = 2; 540 break; 541 542 case CIPHER_CCMP: 543 wsec = 4; 544 break; 545 546 default: 547 wsec = 0; 548 break; 549 } 550 551 switch (params->key_mgmt_suite) { 552 case KEY_MGMT_802_1X: 553 wpa_auth = 1; 554 break; 555 556 case KEY_MGMT_PSK: 557 wpa_auth = 2; 558 break; 559 560 default: 561 wpa_auth = 255; 562 break; 563 } 564 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 */ 569 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; 579 580 return 0; 581 } 582 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, 599 }; 600