1/*
2 * WPA Supplicant - driver interaction with 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
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
38struct 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
68typedef wl_wsec_key_t wsec_key_t;
69#endif
70
71typedef struct {
72	uint32 val;
73	struct ether_addr ea;
74	uint16 res;
75} wlc_deauth_t;
76
77
78static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
79					     void *timeout_ctx);
80
81static 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
107static 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
117static 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
129static 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
160static 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
223static 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
292static 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
350static 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
360static 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
373static 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
388static 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
395static 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
416static const int frequency_list[] = {
417	2412, 2417, 2422, 2427, 2432, 2437, 2442,
418	2447, 2452, 2457, 2462, 2467, 2472, 2484
419};
420
421struct 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
429static int
430wpa_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
498static 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
510static 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
517static int
518wpa_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
583const 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