wpa_supplicant: Initial Revision 0.8.X

Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date:   Thu Apr 21 20:41:01 2011 +0300

    Enable CONFIG_DRIVER_NL80211=y in the default configuration

    nl80211 should be preferred over WEXT with any recent Linux
    kernel version.

Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c
new file mode 100644
index 0000000..cb88543
--- /dev/null
+++ b/src/drivers/driver_broadcom.c
@@ -0,0 +1,599 @@
+/*
+ * WPA Supplicant - driver interaction with old Broadcom wl.o driver
+ * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru>
+ * Copyright (c) 2004, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+ * Linux wireless extensions and does not need (or even work) with this old
+ * driver wrapper. Use driver_wext.c with that driver.
+ */
+
+#include "includes.h"
+
+#include <sys/ioctl.h>
+
+#include "common.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 "driver.h"
+#include "eloop.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_strlcpy(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(const char *ifname, void *priv,
+				       enum 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;
+	u8 *resp_ies = NULL;
+
+	if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) 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) {
+			resp_ies = os_malloc(left);
+			if (resp_ies == NULL)
+				return;
+			os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left);
+			data.assoc_info.resp_ies = resp_ies;
+			data.assoc_info.resp_ies_len = left;
+		}
+
+		wpa_supplicant_event(ctx, EVENT_ASSOC, &data);
+		os_free(resp_ies);
+		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;
+	}
+}	
+
+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_strlcpy(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_strlcpy(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;
+	wpa_driver_broadcom_set_wpa(drv, 1);
+
+	return drv;
+}
+
+static void wpa_driver_broadcom_deinit(void *priv)
+{
+	struct wpa_driver_broadcom_data *drv = priv;
+	wpa_driver_broadcom_set_wpa(drv, 0);
+	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,
+				    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_broadcom_data *drv = priv;
+	wlc_ssid_t wst = { 0, "" };
+	const u8 *ssid = params->ssids[0].ssid;
+	size_t ssid_len = params->ssids[0].ssid_len;
+
+	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 struct wpa_scan_results *
+wpa_driver_broadcom_get_scan_results(void *priv)
+{
+	struct wpa_driver_broadcom_data *drv = priv;
+	char *buf;
+	wl_scan_results_t *wsr;
+	wl_bss_info_t *wbi;
+	size_t ap_num;
+	struct wpa_scan_results *res;
+
+	buf = os_malloc(WLC_IOCTL_MAXLEN);
+	if (buf == NULL)
+		return NULL;
+
+	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 NULL;
+	}
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL) {
+		os_free(buf);
+		return NULL;
+	}
+
+	res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *));
+	if (res->res == NULL) {
+		os_free(res);
+		os_free(buf);
+		return NULL;
+	}
+
+	for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
+		struct wpa_scan_res *r;
+		r = os_malloc(sizeof(*r) + wbi->ie_length);
+		if (r == NULL)
+			break;
+		res->res[res->num++] = r;
+
+		os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN);
+		r->freq = frequency_list[wbi->channel - 1];
+		/* get ie's */
+		os_memcpy(r + 1, wbi + 1, wbi->ie_length);
+		r->ie_len = wbi->ie_length;
+
+		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 res;
+	}
+
+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, NULL, 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;
+	int ret;
+
+	ret = wpa_driver_broadcom_set_drop_unencrypted(
+		drv, params->drop_unencrypted);
+
+	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 ret;
+}
+
+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_key = wpa_driver_broadcom_set_key,
+	.init = wpa_driver_broadcom_init,
+	.deinit = wpa_driver_broadcom_deinit,
+	.set_countermeasures = wpa_driver_broadcom_set_countermeasures,
+	.scan2 = wpa_driver_broadcom_scan,
+	.get_scan_results2 = wpa_driver_broadcom_get_scan_results,
+	.deauthenticate = wpa_driver_broadcom_deauthenticate,
+	.disassociate = wpa_driver_broadcom_disassociate,
+	.associate = wpa_driver_broadcom_associate,
+};