• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/wpa_supplicant/src/drivers/
1/*
2 * WPA Supplicant - driver interaction with old 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 * 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
43struct 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
73typedef wl_wsec_key_t wsec_key_t;
74#endif
75
76typedef struct {
77	uint32 val;
78	struct ether_addr ea;
79	uint16 res;
80} wlc_deauth_t;
81
82
83static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
84					     void *timeout_ctx);
85
86static 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
112static 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
122static 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
134static 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
165static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg,
166				       const u8 *addr, int key_idx, int set_tx,
167				       const u8 *seq, size_t seq_len,
168				       const u8 *key, size_t key_len)
169{
170	struct wpa_driver_broadcom_data *drv = priv;
171	int ret;
172	wsec_key_t wkt;
173
174	os_memset(&wkt, 0, sizeof wkt);
175	wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d",
176		   set_tx ? "PRIMARY " : "", key_idx, alg);
177	if (key && key_len > 0)
178		wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len);
179
180	switch (alg) {
181	case WPA_ALG_NONE:
182		wkt.algo = CRYPTO_ALGO_OFF;
183		break;
184	case WPA_ALG_WEP:
185		wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
186		break;
187	case WPA_ALG_TKIP:
188		wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
189		break;
190	case WPA_ALG_CCMP:
191		wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
192			       * AES_OCB_MSDU, AES_OCB_MPDU? */
193		break;
194	default:
195		wkt.algo = CRYPTO_ALGO_NALG;
196		break;
197	}
198
199	if (seq && seq_len > 0)
200		wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
201
202	if (addr)
203		wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
204
205	wkt.index = key_idx;
206	wkt.len = key_len;
207	if (key && key_len > 0) {
208		os_memcpy(wkt.data, key, key_len);
209		if (key_len == 32) {
210			/* hack hack hack XXX */
211			os_memcpy(&wkt.data[16], &key[24], 8);
212			os_memcpy(&wkt.data[24], &key[16], 8);
213		}
214	}
215	/* wkt.algo = CRYPTO_ALGO_...; */
216	wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
217	if (addr && set_tx)
218		os_memcpy(&wkt.ea, addr, sizeof(wkt.ea));
219	ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt));
220	if (addr && set_tx) {
221		/* FIX: magic number / error handling? */
222		broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea));
223	}
224	return ret;
225}
226
227
228static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
229					      void *sock_ctx)
230{
231	char buf[8192];
232	int left;
233	wl_wpa_header_t *wwh;
234	union wpa_event_data data;
235
236	if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
237		return;
238
239	wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
240
241	if ((size_t) left < sizeof(wl_wpa_header_t))
242		return;
243
244	wwh = (wl_wpa_header_t *) buf;
245
246	if (wwh->snap.type != WL_WPA_ETHER_TYPE)
247		return;
248	if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
249		return;
250
251	os_memset(&data, 0, sizeof(data));
252
253	switch (wwh->type) {
254	case WLC_ASSOC_MSG:
255		left -= WL_WPA_HEADER_LEN;
256		wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
257			   left);
258		if (left > 0) {
259			data.assoc_info.resp_ies = os_malloc(left);
260			if (data.assoc_info.resp_ies == NULL)
261				return;
262			os_memcpy(data.assoc_info.resp_ies,
263				  buf + WL_WPA_HEADER_LEN, left);
264			data.assoc_info.resp_ies_len = left;
265			wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes "
266				    "into resp_ies",
267				    data.assoc_info.resp_ies, left);
268		}
269		/* data.assoc_info.req_ies = NULL; */
270		/* data.assoc_info.req_ies_len = 0; */
271
272		wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
273		wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
274		break;
275	case WLC_DISASSOC_MSG:
276		wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
277		wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
278		break;
279	case WLC_PTK_MIC_MSG:
280		wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE");
281		data.michael_mic_failure.unicast = 1;
282		wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
283		break;
284	case WLC_GTK_MIC_MSG:
285		wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE");
286		data.michael_mic_failure.unicast = 0;
287		wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
288		break;
289	default:
290		wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
291			   wwh->type);
292		break;
293	}
294	os_free(data.assoc_info.resp_ies);
295}
296
297static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
298{
299	int s;
300	struct sockaddr_ll ll;
301	struct wpa_driver_broadcom_data *drv;
302	struct ifreq ifr;
303
304	/* open socket to kernel */
305	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
306		perror("socket");
307		return NULL;
308	}
309	/* do it */
310	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
311	if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
312		perror(ifr.ifr_name);
313		return NULL;
314	}
315
316
317	drv = os_zalloc(sizeof(*drv));
318	if (drv == NULL)
319		return NULL;
320	drv->ctx = ctx;
321	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
322	drv->ioctl_sock = s;
323
324	s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
325	if (s < 0) {
326		perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
327		close(drv->ioctl_sock);
328		os_free(drv);
329		return NULL;
330	}
331
332	os_memset(&ll, 0, sizeof(ll));
333	ll.sll_family = AF_PACKET;
334	ll.sll_protocol = ntohs(ETH_P_802_2);
335	ll.sll_ifindex = ifr.ifr_ifindex;
336	ll.sll_hatype = 0;
337	ll.sll_pkttype = PACKET_HOST;
338	ll.sll_halen = 0;
339
340	if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
341		perror("bind(netlink)");
342		close(s);
343		close(drv->ioctl_sock);
344		os_free(drv);
345		return NULL;
346	}
347
348	eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
349				 NULL);
350	drv->event_sock = s;
351
352	return drv;
353}
354
355static void wpa_driver_broadcom_deinit(void *priv)
356{
357	struct wpa_driver_broadcom_data *drv = priv;
358	eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
359	eloop_unregister_read_sock(drv->event_sock);
360	close(drv->event_sock);
361	close(drv->ioctl_sock);
362	os_free(drv);
363}
364
365static int wpa_driver_broadcom_set_countermeasures(void *priv,
366						   int enabled)
367{
368#if 0
369	struct wpa_driver_broadcom_data *drv = priv;
370	/* FIX: ? */
371	return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
372			      sizeof(enabled));
373#else
374	return 0;
375#endif
376}
377
378static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
379{
380	struct wpa_driver_broadcom_data *drv = priv;
381	/* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
382	int restrict = (enabled ? 1 : 0);
383
384	if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT,
385			   &restrict, sizeof(restrict)) < 0 ||
386	    broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT,
387			   &restrict, sizeof(restrict)) < 0)
388		return -1;
389
390	return 0;
391}
392
393static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
394					     void *timeout_ctx)
395{
396	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
397	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
398}
399
400static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid,
401				    size_t ssid_len)
402{
403	struct wpa_driver_broadcom_data *drv = priv;
404	wlc_ssid_t wst = { 0, "" };
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
421static const int frequency_list[] = {
422	2412, 2417, 2422, 2427, 2432, 2437, 2442,
423	2447, 2452, 2457, 2462, 2467, 2472, 2484
424};
425
426struct 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
434static int
435wpa_driver_broadcom_get_scan_results(void *priv,
436				     struct wpa_scan_result *results,
437				     size_t max_size)
438{
439	struct wpa_driver_broadcom_data *drv = priv;
440	char *buf;
441	wl_scan_results_t *wsr;
442	wl_bss_info_t *wbi;
443	size_t ap_num;
444
445	buf = os_malloc(WLC_IOCTL_MAXLEN);
446	if (buf == NULL)
447		return -1;
448
449	wsr = (wl_scan_results_t *) buf;
450
451	wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
452	wsr->version = 107;
453	wsr->count = 0;
454
455	if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
456		os_free(buf);
457		return -1;
458	}
459
460	os_memset(results, 0, max_size * sizeof(struct wpa_scan_result));
461
462	for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
463		int left;
464		struct bss_ie_hdr *ie;
465
466		os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN);
467		os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len);
468		results[ap_num].ssid_len = wbi->SSID_len;
469		results[ap_num].freq = frequency_list[wbi->channel - 1];
470		/* get ie's */
471		wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs",
472			    (u8 *) wbi + sizeof(*wbi), wbi->ie_length);
473		ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi));
474		for (left = wbi->ie_length; left > 0;
475		     left -= (ie->len + 2), ie = (struct bss_ie_hdr *)
476			     ((u8 *) ie + 2 + ie->len)) {
477			wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d",
478				   ie->elem_id, ie->len);
479			if (ie->len >= 3)
480				wpa_printf(MSG_MSGDUMP,
481					   "BROADCOM: oui:%02x%02x%02x",
482					   ie->oui[0], ie->oui[1], ie->oui[2]);
483			if (ie->elem_id != 0xdd ||
484			    ie->len < 6 ||
485			    os_memcmp(ie->oui, WPA_OUI, 3) != 0)
486				continue;
487			os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2);
488			results[ap_num].wpa_ie_len = ie->len + 2;
489			break;
490		}
491
492		wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
493	}
494
495	wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
496		   "BSSes)",
497		   wsr->buflen, (unsigned long) ap_num);
498
499	os_free(buf);
500	return ap_num;
501}
502
503static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
504					      int reason_code)
505{
506	struct wpa_driver_broadcom_data *drv = priv;
507	wlc_deauth_t wdt;
508	wdt.val = reason_code;
509	os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
510	wdt.res = 0x7fff;
511	return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
512			      sizeof(wdt));
513}
514
515static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
516					    int reason_code)
517{
518	struct wpa_driver_broadcom_data *drv = priv;
519	return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0);
520}
521
522static int
523wpa_driver_broadcom_associate(void *priv,
524			      struct wpa_driver_associate_params *params)
525{
526	struct wpa_driver_broadcom_data *drv = priv;
527	wlc_ssid_t s;
528	int infra = 1;
529	int auth = 0;
530	int wsec = 4;
531	int dummy;
532	int wpa_auth;
533
534	s.SSID_len = params->ssid_len;
535	os_memcpy(s.SSID, params->ssid, params->ssid_len);
536
537	switch (params->pairwise_suite) {
538	case CIPHER_WEP40:
539	case CIPHER_WEP104:
540		wsec = 1;
541		break;
542
543	case CIPHER_TKIP:
544		wsec = 2;
545		break;
546
547	case CIPHER_CCMP:
548		wsec = 4;
549		break;
550
551	default:
552		wsec = 0;
553		break;
554	}
555
556	switch (params->key_mgmt_suite) {
557	case KEY_MGMT_802_1X:
558		wpa_auth = 1;
559		break;
560
561	case KEY_MGMT_PSK:
562		wpa_auth = 2;
563		break;
564
565	default:
566		wpa_auth = 255;
567		break;
568	}
569
570	/* printf("broadcom_associate: %u %u %u\n", pairwise_suite,
571	 * group_suite, key_mgmt_suite);
572	 * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec));
573	 * wl join uses wlc_sec_wep here, not wlc_set_wsec */
574
575	if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 ||
576	    broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth,
577			   sizeof(wpa_auth)) < 0 ||
578	    broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 ||
579	    broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 ||
580	    broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 ||
581	    broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 ||
582	    broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0)
583		return -1;
584
585	return 0;
586}
587
588const struct wpa_driver_ops wpa_driver_broadcom_ops = {
589	.name = "broadcom",
590	.desc = "Broadcom wl.o driver",
591	.get_bssid = wpa_driver_broadcom_get_bssid,
592	.get_ssid = wpa_driver_broadcom_get_ssid,
593	.set_wpa = wpa_driver_broadcom_set_wpa,
594	.set_key = wpa_driver_broadcom_set_key,
595	.init = wpa_driver_broadcom_init,
596	.deinit = wpa_driver_broadcom_deinit,
597	.set_countermeasures = wpa_driver_broadcom_set_countermeasures,
598	.set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted,
599	.scan = wpa_driver_broadcom_scan,
600	.get_scan_results = wpa_driver_broadcom_get_scan_results,
601	.deauthenticate = wpa_driver_broadcom_deauthenticate,
602	.disassociate = wpa_driver_broadcom_disassociate,
603	.associate = wpa_driver_broadcom_associate,
604};
605