| /* |
| * WPA Supplicant - driver interaction with Broadcom wl.o driver |
| * Copyright (c) 2004, Nikki Chumkov <[email protected]> |
| * Copyright (c) 2004, Jouni Malinen <[email protected]> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * Alternatively, this software may be distributed under the terms of BSD |
| * license. |
| * |
| * See README and COPYING for more details. |
| */ |
| |
| #include "includes.h" |
| #include <sys/ioctl.h> |
| #if 0 |
| #include <netpacket/packet.h> |
| #include <net/ethernet.h> /* the L2 protocols */ |
| #else |
| #include <linux/if_packet.h> |
| #include <linux/if_ether.h> /* The L2 protocols */ |
| #endif |
| #include <net/if.h> |
| #include <typedefs.h> |
| |
| /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys |
| * WRT54G GPL tarball. */ |
| #include <wlioctl.h> |
| |
| #include "common.h" |
| #include "driver.h" |
| #include "eloop.h" |
| #include "wpa_supplicant.h" |
| #include "wpa.h" |
| |
| struct wpa_driver_broadcom_data { |
| void *ctx; |
| int ioctl_sock; |
| int event_sock; |
| char ifname[IFNAMSIZ + 1]; |
| }; |
| |
| |
| #ifndef WLC_DEAUTHENTICATE |
| #define WLC_DEAUTHENTICATE 143 |
| #endif |
| #ifndef WLC_DEAUTHENTICATE_WITH_REASON |
| #define WLC_DEAUTHENTICATE_WITH_REASON 201 |
| #endif |
| #ifndef WLC_SET_TKIP_COUNTERMEASURES |
| #define WLC_SET_TKIP_COUNTERMEASURES 202 |
| #endif |
| |
| #if !defined(PSK_ENABLED) /* NEW driver interface */ |
| #define WL_VERSION 360130 |
| /* wireless authentication bit vector */ |
| #define WPA_ENABLED 1 |
| #define PSK_ENABLED 2 |
| |
| #define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) |
| #define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) |
| #define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) |
| |
| #define WSEC_PRIMARY_KEY WL_PRIMARY_KEY |
| |
| typedef wl_wsec_key_t wsec_key_t; |
| #endif |
| |
| typedef struct { |
| uint32 val; |
| struct ether_addr ea; |
| uint16 res; |
| } wlc_deauth_t; |
| |
| |
| static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, |
| void *timeout_ctx); |
| |
| static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, |
| void *buf, int len) |
| { |
| struct ifreq ifr; |
| wl_ioctl_t ioc; |
| int ret = 0; |
| |
| wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", |
| drv->ifname, cmd, len, buf); |
| /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ |
| |
| ioc.cmd = cmd; |
| ioc.buf = buf; |
| ioc.len = len; |
| os_strncpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); |
| ifr.ifr_data = (caddr_t) &ioc; |
| if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { |
| if (cmd != WLC_GET_MAGIC) |
| perror(ifr.ifr_name); |
| wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", |
| cmd, ret); |
| } |
| |
| return ret; |
| } |
| |
| static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) |
| return 0; |
| |
| os_memset(bssid, 0, ETH_ALEN); |
| return -1; |
| } |
| |
| static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| wlc_ssid_t s; |
| |
| if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) |
| return -1; |
| |
| os_memcpy(ssid, s.SSID, s.SSID_len); |
| return s.SSID_len; |
| } |
| |
| static int wpa_driver_broadcom_set_wpa(void *priv, int enable) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| unsigned int wauth, wsec; |
| struct ether_addr ea; |
| |
| os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); |
| if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == |
| -1 || |
| broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) |
| return -1; |
| |
| if (enable) { |
| wauth = PSK_ENABLED; |
| wsec = TKIP_ENABLED; |
| } else { |
| wauth = 255; |
| wsec &= ~(TKIP_ENABLED | AES_ENABLED); |
| } |
| |
| if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == |
| -1 || |
| broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) |
| return -1; |
| |
| /* FIX: magic number / error handling? */ |
| broadcom_ioctl(drv, 122, &ea, sizeof(ea)); |
| |
| return 0; |
| } |
| |
| static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg, |
| const u8 *addr, int key_idx, int set_tx, |
| const u8 *seq, size_t seq_len, |
| const u8 *key, size_t key_len) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| int ret; |
| wsec_key_t wkt; |
| |
| os_memset(&wkt, 0, sizeof wkt); |
| wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", |
| set_tx ? "PRIMARY " : "", key_idx, alg); |
| if (key && key_len > 0) |
| wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); |
| |
| switch (alg) { |
| case WPA_ALG_NONE: |
| wkt.algo = CRYPTO_ALGO_OFF; |
| break; |
| case WPA_ALG_WEP: |
| wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ |
| break; |
| case WPA_ALG_TKIP: |
| wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ |
| break; |
| case WPA_ALG_CCMP: |
| wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; |
| * AES_OCB_MSDU, AES_OCB_MPDU? */ |
| break; |
| default: |
| wkt.algo = CRYPTO_ALGO_NALG; |
| break; |
| } |
| |
| if (seq && seq_len > 0) |
| wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); |
| |
| if (addr) |
| wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); |
| |
| wkt.index = key_idx; |
| wkt.len = key_len; |
| if (key && key_len > 0) { |
| os_memcpy(wkt.data, key, key_len); |
| if (key_len == 32) { |
| /* hack hack hack XXX */ |
| os_memcpy(&wkt.data[16], &key[24], 8); |
| os_memcpy(&wkt.data[24], &key[16], 8); |
| } |
| } |
| /* wkt.algo = CRYPTO_ALGO_...; */ |
| wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; |
| if (addr && set_tx) |
| os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); |
| ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); |
| if (addr && set_tx) { |
| /* FIX: magic number / error handling? */ |
| broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); |
| } |
| return ret; |
| } |
| |
| |
| static void wpa_driver_broadcom_event_receive(int sock, void *ctx, |
| void *sock_ctx) |
| { |
| char buf[8192]; |
| int left; |
| wl_wpa_header_t *wwh; |
| union wpa_event_data data; |
| |
| if ((left = recv(sock, buf, sizeof buf, 0)) < 0) |
| return; |
| |
| wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", buf, left); |
| |
| if ((size_t) left < sizeof(wl_wpa_header_t)) |
| return; |
| |
| wwh = (wl_wpa_header_t *) buf; |
| |
| if (wwh->snap.type != WL_WPA_ETHER_TYPE) |
| return; |
| if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) |
| return; |
| |
| os_memset(&data, 0, sizeof(data)); |
| |
| switch (wwh->type) { |
| case WLC_ASSOC_MSG: |
| left -= WL_WPA_HEADER_LEN; |
| wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", |
| left); |
| if (left > 0) { |
| data.assoc_info.resp_ies = os_malloc(left); |
| if (data.assoc_info.resp_ies == NULL) |
| return; |
| os_memcpy(data.assoc_info.resp_ies, |
| buf + WL_WPA_HEADER_LEN, left); |
| data.assoc_info.resp_ies_len = left; |
| wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " |
| "into resp_ies", |
| data.assoc_info.resp_ies, left); |
| } |
| /* data.assoc_info.req_ies = NULL; */ |
| /* data.assoc_info.req_ies_len = 0; */ |
| |
| wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); |
| wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); |
| break; |
| case WLC_DISASSOC_MSG: |
| wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); |
| wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); |
| break; |
| case WLC_PTK_MIC_MSG: |
| wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); |
| data.michael_mic_failure.unicast = 1; |
| wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); |
| break; |
| case WLC_GTK_MIC_MSG: |
| wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); |
| data.michael_mic_failure.unicast = 0; |
| wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); |
| break; |
| default: |
| wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", |
| wwh->type); |
| break; |
| } |
| os_free(data.assoc_info.resp_ies); |
| } |
| |
| static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) |
| { |
| int s; |
| struct sockaddr_ll ll; |
| struct wpa_driver_broadcom_data *drv; |
| struct ifreq ifr; |
| |
| /* open socket to kernel */ |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
| perror("socket"); |
| return NULL; |
| } |
| /* do it */ |
| os_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); |
| if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { |
| perror(ifr.ifr_name); |
| return NULL; |
| } |
| |
| |
| drv = os_zalloc(sizeof(*drv)); |
| if (drv == NULL) |
| return NULL; |
| drv->ctx = ctx; |
| os_strncpy(drv->ifname, ifname, sizeof(drv->ifname)); |
| drv->ioctl_sock = s; |
| |
| s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); |
| if (s < 0) { |
| perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); |
| close(drv->ioctl_sock); |
| os_free(drv); |
| return NULL; |
| } |
| |
| os_memset(&ll, 0, sizeof(ll)); |
| ll.sll_family = AF_PACKET; |
| ll.sll_protocol = ntohs(ETH_P_802_2); |
| ll.sll_ifindex = ifr.ifr_ifindex; |
| ll.sll_hatype = 0; |
| ll.sll_pkttype = PACKET_HOST; |
| ll.sll_halen = 0; |
| |
| if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { |
| perror("bind(netlink)"); |
| close(s); |
| close(drv->ioctl_sock); |
| os_free(drv); |
| return NULL; |
| } |
| |
| eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, |
| NULL); |
| drv->event_sock = s; |
| |
| return drv; |
| } |
| |
| static void wpa_driver_broadcom_deinit(void *priv) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); |
| eloop_unregister_read_sock(drv->event_sock); |
| close(drv->event_sock); |
| close(drv->ioctl_sock); |
| os_free(drv); |
| } |
| |
| static int wpa_driver_broadcom_set_countermeasures(void *priv, |
| int enabled) |
| { |
| #if 0 |
| struct wpa_driver_broadcom_data *drv = priv; |
| /* FIX: ? */ |
| return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, |
| sizeof(enabled)); |
| #else |
| return 0; |
| #endif |
| } |
| |
| static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ |
| int restrict = (enabled ? 1 : 0); |
| |
| if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, |
| &restrict, sizeof(restrict)) < 0 || |
| broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, |
| &restrict, sizeof(restrict)) < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, |
| void *timeout_ctx) |
| { |
| wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); |
| wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); |
| } |
| |
| static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, |
| size_t ssid_len) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| wlc_ssid_t wst = { 0, "" }; |
| |
| if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { |
| wst.SSID_len = ssid_len; |
| os_memcpy(wst.SSID, ssid, ssid_len); |
| } |
| |
| if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) |
| return -1; |
| |
| eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); |
| eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, |
| drv->ctx); |
| return 0; |
| } |
| |
| |
| static const int frequency_list[] = { |
| 2412, 2417, 2422, 2427, 2432, 2437, 2442, |
| 2447, 2452, 2457, 2462, 2467, 2472, 2484 |
| }; |
| |
| struct bss_ie_hdr { |
| u8 elem_id; |
| u8 len; |
| u8 oui[3]; |
| /* u8 oui_type; */ |
| /* u16 version; */ |
| } __attribute__ ((packed)); |
| |
| static int |
| wpa_driver_broadcom_get_scan_results(void *priv, |
| struct wpa_scan_result *results, |
| size_t max_size) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| char *buf; |
| wl_scan_results_t *wsr = (wl_scan_results_t *) buf; |
| wl_bss_info_t *wbi; |
| size_t ap_num; |
| |
| buf = os_malloc(WLC_IOCTL_MAXLEN); |
| if (buf == NULL) |
| return -1; |
| |
| wsr = (wl_scan_results_t *) buf; |
| |
| wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); |
| wsr->version = 107; |
| wsr->count = 0; |
| |
| if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { |
| os_free(buf); |
| return -1; |
| } |
| |
| os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); |
| |
| for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { |
| int left; |
| struct bss_ie_hdr *ie; |
| |
| os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); |
| os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); |
| results[ap_num].ssid_len = wbi->SSID_len; |
| results[ap_num].freq = frequency_list[wbi->channel - 1]; |
| /* get ie's */ |
| wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", |
| (u8 *) wbi + sizeof(*wbi), wbi->ie_length); |
| ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); |
| for (left = wbi->ie_length; left > 0; |
| left -= (ie->len + 2), ie = (struct bss_ie_hdr *) |
| ((u8 *) ie + 2 + ie->len)) { |
| wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", |
| ie->elem_id, ie->len); |
| if (ie->len >= 3) |
| wpa_printf(MSG_MSGDUMP, |
| "BROADCOM: oui:%02x%02x%02x", |
| ie->oui[0], ie->oui[1], ie->oui[2]); |
| if (ie->elem_id != 0xdd || |
| ie->len < 6 || |
| os_memcmp(ie->oui, WPA_OUI, 3) != 0) |
| continue; |
| os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); |
| results[ap_num].wpa_ie_len = ie->len + 2; |
| break; |
| } |
| |
| wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); |
| } |
| |
| wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " |
| "BSSes)", |
| wsr->buflen, (unsigned long) ap_num); |
| |
| os_free(buf); |
| return ap_num; |
| } |
| |
| static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, |
| int reason_code) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| wlc_deauth_t wdt; |
| wdt.val = reason_code; |
| os_memcpy(&wdt.ea, addr, sizeof wdt.ea); |
| wdt.res = 0x7fff; |
| return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, |
| sizeof(wdt)); |
| } |
| |
| static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, |
| int reason_code) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); |
| } |
| |
| static int |
| wpa_driver_broadcom_associate(void *priv, |
| struct wpa_driver_associate_params *params) |
| { |
| struct wpa_driver_broadcom_data *drv = priv; |
| wlc_ssid_t s; |
| int infra = 1; |
| int auth = 0; |
| int wsec = 4; |
| int dummy; |
| int wpa_auth; |
| |
| s.SSID_len = params->ssid_len; |
| os_memcpy(s.SSID, params->ssid, params->ssid_len); |
| |
| switch (params->pairwise_suite) { |
| case CIPHER_WEP40: |
| case CIPHER_WEP104: |
| wsec = 1; |
| break; |
| |
| case CIPHER_TKIP: |
| wsec = 2; |
| break; |
| |
| case CIPHER_CCMP: |
| wsec = 4; |
| break; |
| |
| default: |
| wsec = 0; |
| break; |
| } |
| |
| switch (params->key_mgmt_suite) { |
| case KEY_MGMT_802_1X: |
| wpa_auth = 1; |
| break; |
| |
| case KEY_MGMT_PSK: |
| wpa_auth = 2; |
| break; |
| |
| default: |
| wpa_auth = 255; |
| break; |
| } |
| |
| /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, |
| * group_suite, key_mgmt_suite); |
| * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); |
| * wl join uses wlc_sec_wep here, not wlc_set_wsec */ |
| |
| if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || |
| broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, |
| sizeof(wpa_auth)) < 0 || |
| broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || |
| broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || |
| broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || |
| broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || |
| broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| const struct wpa_driver_ops wpa_driver_broadcom_ops = { |
| .name = "broadcom", |
| .desc = "Broadcom wl.o driver", |
| .get_bssid = wpa_driver_broadcom_get_bssid, |
| .get_ssid = wpa_driver_broadcom_get_ssid, |
| .set_wpa = wpa_driver_broadcom_set_wpa, |
| .set_key = wpa_driver_broadcom_set_key, |
| .init = wpa_driver_broadcom_init, |
| .deinit = wpa_driver_broadcom_deinit, |
| .set_countermeasures = wpa_driver_broadcom_set_countermeasures, |
| .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted, |
| .scan = wpa_driver_broadcom_scan, |
| .get_scan_results = wpa_driver_broadcom_get_scan_results, |
| .deauthenticate = wpa_driver_broadcom_deauthenticate, |
| .disassociate = wpa_driver_broadcom_disassociate, |
| .associate = wpa_driver_broadcom_associate, |
| }; |