13157ba21SRui Paulo /*
23157ba21SRui Paulo  * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
33157ba21SRui Paulo  * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
43157ba21SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
73157ba21SRui Paulo  *
83157ba21SRui Paulo  * This implementation requires Windows specific event loop implementation,
93157ba21SRui Paulo  * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
103157ba21SRui Paulo  * driver_ndis.c, so only that driver interface can be used and
113157ba21SRui Paulo  * CONFIG_USE_NDISUIO must be defined.
123157ba21SRui Paulo  *
133157ba21SRui Paulo  * WinXP version of the code uses overlapped I/O and a single threaded design
143157ba21SRui Paulo  * with callback functions from I/O code. WinCE version uses a separate RX
153157ba21SRui Paulo  * thread that blocks on ReadFile() whenever the media status is connected.
163157ba21SRui Paulo  */
173157ba21SRui Paulo 
183157ba21SRui Paulo #include "includes.h"
193157ba21SRui Paulo #include <winsock2.h>
203157ba21SRui Paulo #include <ntddndis.h>
213157ba21SRui Paulo 
223157ba21SRui Paulo #ifdef _WIN32_WCE
233157ba21SRui Paulo #include <winioctl.h>
243157ba21SRui Paulo #include <nuiouser.h>
253157ba21SRui Paulo #endif /* _WIN32_WCE */
263157ba21SRui Paulo 
273157ba21SRui Paulo #include "common.h"
283157ba21SRui Paulo #include "eloop.h"
293157ba21SRui Paulo #include "l2_packet.h"
303157ba21SRui Paulo 
313157ba21SRui Paulo #ifndef _WIN32_WCE
323157ba21SRui Paulo /* from nuiouser.h */
333157ba21SRui Paulo #define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
343157ba21SRui Paulo #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
353157ba21SRui Paulo 	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
363157ba21SRui Paulo #define IOCTL_NDISUIO_SET_ETHER_TYPE \
373157ba21SRui Paulo 	_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
383157ba21SRui Paulo 			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
393157ba21SRui Paulo #endif /* _WIN32_WCE */
403157ba21SRui Paulo 
413157ba21SRui Paulo /* From driver_ndis.c to shared the handle to NDISUIO */
423157ba21SRui Paulo HANDLE driver_ndis_get_ndisuio_handle(void);
433157ba21SRui Paulo 
443157ba21SRui Paulo /*
453157ba21SRui Paulo  * NDISUIO supports filtering of only one ethertype at the time, so we must
463157ba21SRui Paulo  * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
473157ba21SRui Paulo  * whenever wpa_supplicant is trying to pre-authenticate and then switching
483157ba21SRui Paulo  * back to EAPOL when pre-authentication has been completed.
493157ba21SRui Paulo  */
503157ba21SRui Paulo 
513157ba21SRui Paulo struct l2_packet_data;
523157ba21SRui Paulo 
533157ba21SRui Paulo struct l2_packet_ndisuio_global {
543157ba21SRui Paulo 	int refcount;
553157ba21SRui Paulo 	unsigned short first_proto;
563157ba21SRui Paulo 	struct l2_packet_data *l2[2];
573157ba21SRui Paulo #ifdef _WIN32_WCE
583157ba21SRui Paulo 	HANDLE rx_thread;
593157ba21SRui Paulo 	HANDLE stop_request;
603157ba21SRui Paulo 	HANDLE ready_for_read;
613157ba21SRui Paulo 	HANDLE rx_processed;
623157ba21SRui Paulo #endif /* _WIN32_WCE */
633157ba21SRui Paulo };
643157ba21SRui Paulo 
653157ba21SRui Paulo static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
663157ba21SRui Paulo 
673157ba21SRui Paulo struct l2_packet_data {
683157ba21SRui Paulo 	char ifname[100];
693157ba21SRui Paulo 	u8 own_addr[ETH_ALEN];
703157ba21SRui Paulo 	void (*rx_callback)(void *ctx, const u8 *src_addr,
713157ba21SRui Paulo 			    const u8 *buf, size_t len);
723157ba21SRui Paulo 	void *rx_callback_ctx;
733157ba21SRui Paulo 	int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
743157ba21SRui Paulo 		     * rx_callback and l2_packet_send() */
753157ba21SRui Paulo 	HANDLE rx_avail;
763157ba21SRui Paulo #ifndef _WIN32_WCE
773157ba21SRui Paulo 	OVERLAPPED rx_overlapped;
783157ba21SRui Paulo #endif /* _WIN32_WCE */
793157ba21SRui Paulo 	u8 rx_buf[1514];
803157ba21SRui Paulo 	DWORD rx_written;
813157ba21SRui Paulo };
823157ba21SRui Paulo 
833157ba21SRui Paulo 
l2_packet_get_own_addr(struct l2_packet_data * l2,u8 * addr)843157ba21SRui Paulo int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
853157ba21SRui Paulo {
863157ba21SRui Paulo 	os_memcpy(addr, l2->own_addr, ETH_ALEN);
873157ba21SRui Paulo 	return 0;
883157ba21SRui Paulo }
893157ba21SRui Paulo 
903157ba21SRui Paulo 
l2_packet_send(struct l2_packet_data * l2,const u8 * dst_addr,u16 proto,const u8 * buf,size_t len)913157ba21SRui Paulo int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
923157ba21SRui Paulo 		   const u8 *buf, size_t len)
933157ba21SRui Paulo {
943157ba21SRui Paulo 	BOOL res;
953157ba21SRui Paulo 	DWORD written;
963157ba21SRui Paulo 	struct l2_ethhdr *eth;
973157ba21SRui Paulo #ifndef _WIN32_WCE
983157ba21SRui Paulo 	OVERLAPPED overlapped;
993157ba21SRui Paulo #endif /* _WIN32_WCE */
1003157ba21SRui Paulo 	OVERLAPPED *o;
1013157ba21SRui Paulo 
1023157ba21SRui Paulo 	if (l2 == NULL)
1033157ba21SRui Paulo 		return -1;
1043157ba21SRui Paulo 
1053157ba21SRui Paulo #ifdef _WIN32_WCE
1063157ba21SRui Paulo 	o = NULL;
1073157ba21SRui Paulo #else /* _WIN32_WCE */
1083157ba21SRui Paulo 	os_memset(&overlapped, 0, sizeof(overlapped));
1093157ba21SRui Paulo 	o = &overlapped;
1103157ba21SRui Paulo #endif /* _WIN32_WCE */
1113157ba21SRui Paulo 
1123157ba21SRui Paulo 	if (l2->l2_hdr) {
1133157ba21SRui Paulo 		res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
1143157ba21SRui Paulo 				&written, o);
1153157ba21SRui Paulo 	} else {
1163157ba21SRui Paulo 		size_t mlen = sizeof(*eth) + len;
1173157ba21SRui Paulo 		eth = os_malloc(mlen);
1183157ba21SRui Paulo 		if (eth == NULL)
1193157ba21SRui Paulo 			return -1;
1203157ba21SRui Paulo 
1213157ba21SRui Paulo 		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
1223157ba21SRui Paulo 		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
1233157ba21SRui Paulo 		eth->h_proto = htons(proto);
1243157ba21SRui Paulo 		os_memcpy(eth + 1, buf, len);
1253157ba21SRui Paulo 		res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
1263157ba21SRui Paulo 				&written, o);
1273157ba21SRui Paulo 		os_free(eth);
1283157ba21SRui Paulo 	}
1293157ba21SRui Paulo 
1303157ba21SRui Paulo 	if (!res) {
1313157ba21SRui Paulo 		DWORD err = GetLastError();
1323157ba21SRui Paulo #ifndef _WIN32_WCE
1333157ba21SRui Paulo 		if (err == ERROR_IO_PENDING) {
134e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
135e28a4053SRui Paulo 				   "write to complete");
136e28a4053SRui Paulo 			res = GetOverlappedResult(
137e28a4053SRui Paulo 				driver_ndis_get_ndisuio_handle(), &overlapped,
138e28a4053SRui Paulo 				&written, TRUE);
139e28a4053SRui Paulo 			if (!res) {
140e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
141e28a4053SRui Paulo 					   "GetOverlappedResult failed: %d",
142e28a4053SRui Paulo 					   (int) GetLastError());
143e28a4053SRui Paulo 				return -1;
144e28a4053SRui Paulo 			}
1453157ba21SRui Paulo 			return 0;
1463157ba21SRui Paulo 		}
1473157ba21SRui Paulo #endif /* _WIN32_WCE */
1483157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
1493157ba21SRui Paulo 			   (int) GetLastError());
1503157ba21SRui Paulo 		return -1;
1513157ba21SRui Paulo 	}
1523157ba21SRui Paulo 
1533157ba21SRui Paulo 	return 0;
1543157ba21SRui Paulo }
1553157ba21SRui Paulo 
1563157ba21SRui Paulo 
1573157ba21SRui Paulo static void l2_packet_callback(struct l2_packet_data *l2);
1583157ba21SRui Paulo 
1593157ba21SRui Paulo #ifdef _WIN32_WCE
l2_packet_rx_thread_try_read(struct l2_packet_data * l2)1603157ba21SRui Paulo static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
1613157ba21SRui Paulo {
1623157ba21SRui Paulo 	HANDLE handles[2];
1633157ba21SRui Paulo 
1643157ba21SRui Paulo 	wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
1653157ba21SRui Paulo 	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
1663157ba21SRui Paulo 		      sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
1673157ba21SRui Paulo 		DWORD err = GetLastError();
1683157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
1693157ba21SRui Paulo 			   "%d", (int) err);
1703157ba21SRui Paulo 		/*
1713157ba21SRui Paulo 		 * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
1723157ba21SRui Paulo 		 * error whenever the connection is not up. Yield the thread to
1733157ba21SRui Paulo 		 * avoid triggering a busy loop. Connection event should stop
1743157ba21SRui Paulo 		 * us from looping for long, but we need to allow enough CPU
1753157ba21SRui Paulo 		 * for the main thread to process the media disconnection.
1763157ba21SRui Paulo 		 */
1773157ba21SRui Paulo 		Sleep(100);
1783157ba21SRui Paulo 		return;
1793157ba21SRui Paulo 	}
1803157ba21SRui Paulo 
1813157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
1823157ba21SRui Paulo 		   (int) l2->rx_written);
1833157ba21SRui Paulo 
1843157ba21SRui Paulo 	/*
1853157ba21SRui Paulo 	 * Notify the main thread about the availability of a frame and wait
1863157ba21SRui Paulo 	 * for the frame to be processed.
1873157ba21SRui Paulo 	 */
1883157ba21SRui Paulo 	SetEvent(l2->rx_avail);
1893157ba21SRui Paulo 	handles[0] = l2_ndisuio_global->stop_request;
1903157ba21SRui Paulo 	handles[1] = l2_ndisuio_global->rx_processed;
1913157ba21SRui Paulo 	WaitForMultipleObjects(2, handles, FALSE, INFINITE);
1923157ba21SRui Paulo 	ResetEvent(l2_ndisuio_global->rx_processed);
1933157ba21SRui Paulo }
1943157ba21SRui Paulo 
1953157ba21SRui Paulo 
l2_packet_rx_thread(LPVOID arg)1963157ba21SRui Paulo static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
1973157ba21SRui Paulo {
1983157ba21SRui Paulo 	struct l2_packet_data *l2 = arg;
1993157ba21SRui Paulo 	DWORD res;
2003157ba21SRui Paulo 	HANDLE handles[2];
2013157ba21SRui Paulo 	int run = 1;
2023157ba21SRui Paulo 
2033157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
2043157ba21SRui Paulo 	handles[0] = l2_ndisuio_global->stop_request;
2053157ba21SRui Paulo 	handles[1] = l2_ndisuio_global->ready_for_read;
2063157ba21SRui Paulo 
2073157ba21SRui Paulo 	/*
2083157ba21SRui Paulo 	 * Unfortunately, NDISUIO on WinCE does not seem to support waiting
2093157ba21SRui Paulo 	 * on the handle. There do not seem to be anything else that we could
2103157ba21SRui Paulo 	 * wait for either. If one were to modify NDISUIO to set a named event
2113157ba21SRui Paulo 	 * whenever packets are available, this event could be used here to
2123157ba21SRui Paulo 	 * avoid having to poll for new packets or we could even move to use a
2133157ba21SRui Paulo 	 * single threaded design.
2143157ba21SRui Paulo 	 *
2153157ba21SRui Paulo 	 * In addition, NDISUIO on WinCE is returning
2163157ba21SRui Paulo 	 * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
2173157ba21SRui Paulo 	 * the adapter is not in connected state. For now, we are just using a
2183157ba21SRui Paulo 	 * local event to allow ReadFile calls only after having received NDIS
2193157ba21SRui Paulo 	 * media connect event. This event could be easily converted to handle
2203157ba21SRui Paulo 	 * another event if the protocol driver is replaced with somewhat more
2213157ba21SRui Paulo 	 * useful design.
2223157ba21SRui Paulo 	 */
2233157ba21SRui Paulo 
2243157ba21SRui Paulo 	while (l2_ndisuio_global && run) {
2253157ba21SRui Paulo 		res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
2263157ba21SRui Paulo 		switch (res) {
2273157ba21SRui Paulo 		case WAIT_OBJECT_0:
2283157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
2293157ba21SRui Paulo 				   "request to stop RX thread");
2303157ba21SRui Paulo 			run = 0;
2313157ba21SRui Paulo 			break;
2323157ba21SRui Paulo 		case WAIT_OBJECT_0 + 1:
2333157ba21SRui Paulo 			l2_packet_rx_thread_try_read(l2);
2343157ba21SRui Paulo 			break;
2353157ba21SRui Paulo 		case WAIT_FAILED:
2363157ba21SRui Paulo 		default:
2373157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
2383157ba21SRui Paulo 				   "WaitForMultipleObjects failed: %d",
2393157ba21SRui Paulo 				   (int) GetLastError());
2403157ba21SRui Paulo 			run = 0;
2413157ba21SRui Paulo 			break;
2423157ba21SRui Paulo 		}
2433157ba21SRui Paulo 	}
2443157ba21SRui Paulo 
2453157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
2463157ba21SRui Paulo 
2473157ba21SRui Paulo 	return 0;
2483157ba21SRui Paulo }
2493157ba21SRui Paulo #else /* _WIN32_WCE */
l2_ndisuio_start_read(struct l2_packet_data * l2,int recursive)2503157ba21SRui Paulo static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
2513157ba21SRui Paulo {
2523157ba21SRui Paulo 	os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
2533157ba21SRui Paulo 	l2->rx_overlapped.hEvent = l2->rx_avail;
2543157ba21SRui Paulo 	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
2553157ba21SRui Paulo 		      sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
2563157ba21SRui Paulo 	{
2573157ba21SRui Paulo 		DWORD err = GetLastError();
2583157ba21SRui Paulo 		if (err != ERROR_IO_PENDING) {
2593157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
2603157ba21SRui Paulo 				   "%d", (int) err);
2613157ba21SRui Paulo 			return -1;
2623157ba21SRui Paulo 		}
2633157ba21SRui Paulo 		/*
2643157ba21SRui Paulo 		 * Once read is completed, l2_packet_rx_event() will be
2653157ba21SRui Paulo 		 * called.
2663157ba21SRui Paulo 		 */
2673157ba21SRui Paulo 	} else {
2683157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
2693157ba21SRui Paulo 			   "without wait for completion");
2703157ba21SRui Paulo 		if (!recursive)
2713157ba21SRui Paulo 			l2_packet_callback(l2);
2723157ba21SRui Paulo 	}
2733157ba21SRui Paulo 
2743157ba21SRui Paulo 	return 0;
2753157ba21SRui Paulo }
2763157ba21SRui Paulo #endif /* _WIN32_WCE */
2773157ba21SRui Paulo 
2783157ba21SRui Paulo 
l2_packet_callback(struct l2_packet_data * l2)2793157ba21SRui Paulo static void l2_packet_callback(struct l2_packet_data *l2)
2803157ba21SRui Paulo {
2813157ba21SRui Paulo 	const u8 *rx_buf, *rx_src;
2823157ba21SRui Paulo 	size_t rx_len;
2833157ba21SRui Paulo 	struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
2843157ba21SRui Paulo 
2853157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
2863157ba21SRui Paulo 		   (int) l2->rx_written);
2873157ba21SRui Paulo 
2883157ba21SRui Paulo 	if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
2893157ba21SRui Paulo 		rx_buf = (u8 *) ethhdr;
2903157ba21SRui Paulo 		rx_len = l2->rx_written;
2913157ba21SRui Paulo 	} else {
2923157ba21SRui Paulo 		rx_buf = (u8 *) (ethhdr + 1);
2933157ba21SRui Paulo 		rx_len = l2->rx_written - sizeof(*ethhdr);
2943157ba21SRui Paulo 	}
2953157ba21SRui Paulo 	rx_src = ethhdr->h_source;
2963157ba21SRui Paulo 
297*c1d255d3SCy Schubert 	if (l2->rx_callback)
2983157ba21SRui Paulo 		l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
2993157ba21SRui Paulo #ifndef _WIN32_WCE
3003157ba21SRui Paulo 	l2_ndisuio_start_read(l2, 1);
3013157ba21SRui Paulo #endif /* _WIN32_WCE */
3023157ba21SRui Paulo }
3033157ba21SRui Paulo 
3043157ba21SRui Paulo 
l2_packet_rx_event(void * eloop_data,void * user_data)3053157ba21SRui Paulo static void l2_packet_rx_event(void *eloop_data, void *user_data)
3063157ba21SRui Paulo {
3073157ba21SRui Paulo 	struct l2_packet_data *l2 = eloop_data;
3083157ba21SRui Paulo 
3093157ba21SRui Paulo 	if (l2_ndisuio_global)
3103157ba21SRui Paulo 		l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
3113157ba21SRui Paulo 
3123157ba21SRui Paulo 	ResetEvent(l2->rx_avail);
3133157ba21SRui Paulo 
3143157ba21SRui Paulo #ifndef _WIN32_WCE
3153157ba21SRui Paulo 	if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
3163157ba21SRui Paulo 				 &l2->rx_overlapped, &l2->rx_written, FALSE)) {
3173157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
3183157ba21SRui Paulo 			   "failed: %d", (int) GetLastError());
3193157ba21SRui Paulo 		return;
3203157ba21SRui Paulo 	}
3213157ba21SRui Paulo #endif /* _WIN32_WCE */
3223157ba21SRui Paulo 
3233157ba21SRui Paulo 	l2_packet_callback(l2);
3243157ba21SRui Paulo 
3253157ba21SRui Paulo #ifdef _WIN32_WCE
3263157ba21SRui Paulo 	SetEvent(l2_ndisuio_global->rx_processed);
3273157ba21SRui Paulo #endif /* _WIN32_WCE */
3283157ba21SRui Paulo }
3293157ba21SRui Paulo 
3303157ba21SRui Paulo 
l2_ndisuio_set_ether_type(unsigned short protocol)3313157ba21SRui Paulo static int l2_ndisuio_set_ether_type(unsigned short protocol)
3323157ba21SRui Paulo {
3333157ba21SRui Paulo 	USHORT proto = htons(protocol);
3343157ba21SRui Paulo 	DWORD written;
3353157ba21SRui Paulo 
3363157ba21SRui Paulo 	if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
3373157ba21SRui Paulo 			     IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
3383157ba21SRui Paulo 			     sizeof(proto), NULL, 0, &written, NULL)) {
3393157ba21SRui Paulo 		wpa_printf(MSG_ERROR, "L2(NDISUIO): "
3403157ba21SRui Paulo 			   "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
3413157ba21SRui Paulo 			   (int) GetLastError());
3423157ba21SRui Paulo 		return -1;
3433157ba21SRui Paulo 	}
3443157ba21SRui Paulo 
3453157ba21SRui Paulo 	return 0;
3463157ba21SRui Paulo }
3473157ba21SRui Paulo 
3483157ba21SRui Paulo 
l2_packet_init(const char * ifname,const u8 * own_addr,unsigned short protocol,void (* rx_callback)(void * ctx,const u8 * src_addr,const u8 * buf,size_t len),void * rx_callback_ctx,int l2_hdr)3493157ba21SRui Paulo struct l2_packet_data * l2_packet_init(
3503157ba21SRui Paulo 	const char *ifname, const u8 *own_addr, unsigned short protocol,
3513157ba21SRui Paulo 	void (*rx_callback)(void *ctx, const u8 *src_addr,
3523157ba21SRui Paulo 			    const u8 *buf, size_t len),
3533157ba21SRui Paulo 	void *rx_callback_ctx, int l2_hdr)
3543157ba21SRui Paulo {
3553157ba21SRui Paulo 	struct l2_packet_data *l2;
3563157ba21SRui Paulo 
3573157ba21SRui Paulo 	if (l2_ndisuio_global == NULL) {
3583157ba21SRui Paulo 		l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
3593157ba21SRui Paulo 		if (l2_ndisuio_global == NULL)
3603157ba21SRui Paulo 			return NULL;
3613157ba21SRui Paulo 		l2_ndisuio_global->first_proto = protocol;
3623157ba21SRui Paulo 	}
3633157ba21SRui Paulo 	if (l2_ndisuio_global->refcount >= 2) {
3643157ba21SRui Paulo 		wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
3653157ba21SRui Paulo 			   "simultaneous connections allowed");
3663157ba21SRui Paulo 		return NULL;
3673157ba21SRui Paulo 	}
3683157ba21SRui Paulo 	l2_ndisuio_global->refcount++;
3693157ba21SRui Paulo 
3703157ba21SRui Paulo 	l2 = os_zalloc(sizeof(struct l2_packet_data));
3713157ba21SRui Paulo 	if (l2 == NULL)
3723157ba21SRui Paulo 		return NULL;
3733157ba21SRui Paulo 	l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
3743157ba21SRui Paulo 
3753157ba21SRui Paulo 	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
3763157ba21SRui Paulo 	l2->rx_callback = rx_callback;
3773157ba21SRui Paulo 	l2->rx_callback_ctx = rx_callback_ctx;
3783157ba21SRui Paulo 	l2->l2_hdr = l2_hdr;
3793157ba21SRui Paulo 
3803157ba21SRui Paulo 	if (own_addr)
3813157ba21SRui Paulo 		os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
3823157ba21SRui Paulo 
3833157ba21SRui Paulo 	if (l2_ndisuio_set_ether_type(protocol) < 0) {
3843157ba21SRui Paulo 		os_free(l2);
3853157ba21SRui Paulo 		return NULL;
3863157ba21SRui Paulo 	}
3873157ba21SRui Paulo 
3883157ba21SRui Paulo 	if (l2_ndisuio_global->refcount > 1) {
3893157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
3903157ba21SRui Paulo 			   "filtering ethertype to %04x", protocol);
3913157ba21SRui Paulo 		if (l2_ndisuio_global->l2[0])
3923157ba21SRui Paulo 			l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
3933157ba21SRui Paulo 		return l2;
3943157ba21SRui Paulo 	}
3953157ba21SRui Paulo 
3963157ba21SRui Paulo 	l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
3973157ba21SRui Paulo 	if (l2->rx_avail == NULL) {
3983157ba21SRui Paulo 		os_free(l2);
3993157ba21SRui Paulo 		return NULL;
4003157ba21SRui Paulo 	}
4013157ba21SRui Paulo 
4023157ba21SRui Paulo 	eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
4033157ba21SRui Paulo 			     l2_packet_rx_event, l2, NULL);
4043157ba21SRui Paulo 
4053157ba21SRui Paulo #ifdef _WIN32_WCE
4063157ba21SRui Paulo 	l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
4073157ba21SRui Paulo 	/*
4083157ba21SRui Paulo 	 * This event is being set based on media connect/disconnect
4093157ba21SRui Paulo 	 * notifications in driver_ndis.c.
4103157ba21SRui Paulo 	 */
4113157ba21SRui Paulo 	l2_ndisuio_global->ready_for_read =
4123157ba21SRui Paulo 		CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
4133157ba21SRui Paulo 	l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
4143157ba21SRui Paulo 	if (l2_ndisuio_global->stop_request == NULL ||
4153157ba21SRui Paulo 	    l2_ndisuio_global->ready_for_read == NULL ||
4163157ba21SRui Paulo 	    l2_ndisuio_global->rx_processed == NULL) {
4173157ba21SRui Paulo 		if (l2_ndisuio_global->stop_request) {
4183157ba21SRui Paulo 			CloseHandle(l2_ndisuio_global->stop_request);
4193157ba21SRui Paulo 			l2_ndisuio_global->stop_request = NULL;
4203157ba21SRui Paulo 		}
4213157ba21SRui Paulo 		if (l2_ndisuio_global->ready_for_read) {
4223157ba21SRui Paulo 			CloseHandle(l2_ndisuio_global->ready_for_read);
4233157ba21SRui Paulo 			l2_ndisuio_global->ready_for_read = NULL;
4243157ba21SRui Paulo 		}
4253157ba21SRui Paulo 		if (l2_ndisuio_global->rx_processed) {
4263157ba21SRui Paulo 			CloseHandle(l2_ndisuio_global->rx_processed);
4273157ba21SRui Paulo 			l2_ndisuio_global->rx_processed = NULL;
4283157ba21SRui Paulo 		}
4293157ba21SRui Paulo 		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
4303157ba21SRui Paulo 		os_free(l2);
4313157ba21SRui Paulo 		return NULL;
4323157ba21SRui Paulo 	}
4333157ba21SRui Paulo 
4343157ba21SRui Paulo 	l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
4353157ba21SRui Paulo 						    l2_packet_rx_thread, l2, 0,
4363157ba21SRui Paulo 						    NULL);
4373157ba21SRui Paulo 	if (l2_ndisuio_global->rx_thread == NULL) {
4383157ba21SRui Paulo 		wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
4393157ba21SRui Paulo 			   "thread: %d", (int) GetLastError());
4403157ba21SRui Paulo 		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
4413157ba21SRui Paulo 		CloseHandle(l2_ndisuio_global->stop_request);
4423157ba21SRui Paulo 		l2_ndisuio_global->stop_request = NULL;
4433157ba21SRui Paulo 		os_free(l2);
4443157ba21SRui Paulo 		return NULL;
4453157ba21SRui Paulo 	}
4463157ba21SRui Paulo #else /* _WIN32_WCE */
4473157ba21SRui Paulo 	l2_ndisuio_start_read(l2, 0);
4483157ba21SRui Paulo #endif /* _WIN32_WCE */
4493157ba21SRui Paulo 
4503157ba21SRui Paulo 	return l2;
4513157ba21SRui Paulo }
4523157ba21SRui Paulo 
4533157ba21SRui Paulo 
l2_packet_init_bridge(const char * br_ifname,const char * ifname,const u8 * own_addr,unsigned short protocol,void (* rx_callback)(void * ctx,const u8 * src_addr,const u8 * buf,size_t len),void * rx_callback_ctx,int l2_hdr)4545b9c547cSRui Paulo struct l2_packet_data * l2_packet_init_bridge(
4555b9c547cSRui Paulo 	const char *br_ifname, const char *ifname, const u8 *own_addr,
4565b9c547cSRui Paulo 	unsigned short protocol,
4575b9c547cSRui Paulo 	void (*rx_callback)(void *ctx, const u8 *src_addr,
4585b9c547cSRui Paulo 			    const u8 *buf, size_t len),
4595b9c547cSRui Paulo 	void *rx_callback_ctx, int l2_hdr)
4605b9c547cSRui Paulo {
4615b9c547cSRui Paulo 	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
4625b9c547cSRui Paulo 			      rx_callback_ctx, l2_hdr);
4635b9c547cSRui Paulo }
4645b9c547cSRui Paulo 
4655b9c547cSRui Paulo 
l2_packet_deinit(struct l2_packet_data * l2)4663157ba21SRui Paulo void l2_packet_deinit(struct l2_packet_data *l2)
4673157ba21SRui Paulo {
4683157ba21SRui Paulo 	if (l2 == NULL)
4693157ba21SRui Paulo 		return;
4703157ba21SRui Paulo 
4713157ba21SRui Paulo 	if (l2_ndisuio_global) {
4723157ba21SRui Paulo 		l2_ndisuio_global->refcount--;
4733157ba21SRui Paulo 		l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
4743157ba21SRui Paulo 		if (l2_ndisuio_global->refcount) {
4753157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
4763157ba21SRui Paulo 				   "ethertype to %04x",
4773157ba21SRui Paulo 				   l2_ndisuio_global->first_proto);
4783157ba21SRui Paulo 			l2_ndisuio_set_ether_type(
4793157ba21SRui Paulo 				l2_ndisuio_global->first_proto);
4803157ba21SRui Paulo 			return;
4813157ba21SRui Paulo 		}
4823157ba21SRui Paulo 
4833157ba21SRui Paulo #ifdef _WIN32_WCE
4843157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
4853157ba21SRui Paulo 			   "stop");
4863157ba21SRui Paulo 		SetEvent(l2_ndisuio_global->stop_request);
4873157ba21SRui Paulo 		/*
4883157ba21SRui Paulo 		 * Cancel pending ReadFile() in the RX thread (if we were still
4893157ba21SRui Paulo 		 * connected at this point).
4903157ba21SRui Paulo 		 */
4913157ba21SRui Paulo 		if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
4923157ba21SRui Paulo 				     IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
4933157ba21SRui Paulo 				     NULL)) {
4943157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
4953157ba21SRui Paulo 				   "failed: %d", (int) GetLastError());
4963157ba21SRui Paulo 			/* RX thread will exit blocking ReadFile once NDISUIO
4973157ba21SRui Paulo 			 * notices that the adapter is disconnected. */
4983157ba21SRui Paulo 		}
4993157ba21SRui Paulo 		WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
5003157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
5013157ba21SRui Paulo 		CloseHandle(l2_ndisuio_global->rx_thread);
5023157ba21SRui Paulo 		CloseHandle(l2_ndisuio_global->stop_request);
5033157ba21SRui Paulo 		CloseHandle(l2_ndisuio_global->ready_for_read);
5043157ba21SRui Paulo 		CloseHandle(l2_ndisuio_global->rx_processed);
5053157ba21SRui Paulo #endif /* _WIN32_WCE */
5063157ba21SRui Paulo 
5073157ba21SRui Paulo 		os_free(l2_ndisuio_global);
5083157ba21SRui Paulo 		l2_ndisuio_global = NULL;
5093157ba21SRui Paulo 	}
5103157ba21SRui Paulo 
5113157ba21SRui Paulo #ifndef _WIN32_WCE
5123157ba21SRui Paulo 	CancelIo(driver_ndis_get_ndisuio_handle());
5133157ba21SRui Paulo #endif /* _WIN32_WCE */
5143157ba21SRui Paulo 
5153157ba21SRui Paulo 	eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
5163157ba21SRui Paulo 	CloseHandle(l2->rx_avail);
5173157ba21SRui Paulo 	os_free(l2);
5183157ba21SRui Paulo }
5193157ba21SRui Paulo 
5203157ba21SRui Paulo 
l2_packet_get_ip_addr(struct l2_packet_data * l2,char * buf,size_t len)5213157ba21SRui Paulo int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
5223157ba21SRui Paulo {
5233157ba21SRui Paulo 	return -1;
5243157ba21SRui Paulo }
5253157ba21SRui Paulo 
5263157ba21SRui Paulo 
l2_packet_notify_auth_start(struct l2_packet_data * l2)5273157ba21SRui Paulo void l2_packet_notify_auth_start(struct l2_packet_data *l2)
5283157ba21SRui Paulo {
5293157ba21SRui Paulo }
5305b9c547cSRui Paulo 
5315b9c547cSRui Paulo 
l2_packet_set_packet_filter(struct l2_packet_data * l2,enum l2_packet_filter_type type)5325b9c547cSRui Paulo int l2_packet_set_packet_filter(struct l2_packet_data *l2,
5335b9c547cSRui Paulo 				enum l2_packet_filter_type type)
5345b9c547cSRui Paulo {
5355b9c547cSRui Paulo 	return -1;
5365b9c547cSRui Paulo }
537