Home | History | Annotate | Download | only in l2_packet
      1 /*
      2  * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
      3  * Copyright (c) 2003-2006, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  *
     14  * This implementation requires Windows specific event loop implementation,
     15  * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
     16  * driver_ndis.c, so only that driver interface can be used and
     17  * CONFIG_USE_NDISUIO must be defined.
     18  *
     19  * WinXP version of the code uses overlapped I/O and a single threaded design
     20  * with callback functions from I/O code. WinCE version uses a separate RX
     21  * thread that blocks on ReadFile() whenever the media status is connected.
     22  */
     23 
     24 #include "includes.h"
     25 #include <winsock2.h>
     26 #include <ntddndis.h>
     27 
     28 #ifdef _WIN32_WCE
     29 #include <winioctl.h>
     30 #include <nuiouser.h>
     31 #endif /* _WIN32_WCE */
     32 
     33 #include "common.h"
     34 #include "eloop.h"
     35 #include "l2_packet.h"
     36 
     37 #ifndef _WIN32_WCE
     38 /* from nuiouser.h */
     39 #define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
     40 #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
     41 	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
     42 #define IOCTL_NDISUIO_SET_ETHER_TYPE \
     43 	_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
     44 			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
     45 #endif /* _WIN32_WCE */
     46 
     47 /* From driver_ndis.c to shared the handle to NDISUIO */
     48 HANDLE driver_ndis_get_ndisuio_handle(void);
     49 
     50 /*
     51  * NDISUIO supports filtering of only one ethertype at the time, so we must
     52  * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
     53  * whenever wpa_supplicant is trying to pre-authenticate and then switching
     54  * back to EAPOL when pre-authentication has been completed.
     55  */
     56 
     57 struct l2_packet_data;
     58 
     59 struct l2_packet_ndisuio_global {
     60 	int refcount;
     61 	unsigned short first_proto;
     62 	struct l2_packet_data *l2[2];
     63 #ifdef _WIN32_WCE
     64 	HANDLE rx_thread;
     65 	HANDLE stop_request;
     66 	HANDLE ready_for_read;
     67 	HANDLE rx_processed;
     68 #endif /* _WIN32_WCE */
     69 };
     70 
     71 static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
     72 
     73 struct l2_packet_data {
     74 	char ifname[100];
     75 	u8 own_addr[ETH_ALEN];
     76 	void (*rx_callback)(void *ctx, const u8 *src_addr,
     77 			    const u8 *buf, size_t len);
     78 	void *rx_callback_ctx;
     79 	int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
     80 		     * rx_callback and l2_packet_send() */
     81 	HANDLE rx_avail;
     82 #ifndef _WIN32_WCE
     83 	OVERLAPPED rx_overlapped;
     84 #endif /* _WIN32_WCE */
     85 	u8 rx_buf[1514];
     86 	DWORD rx_written;
     87 };
     88 
     89 
     90 int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
     91 {
     92 	os_memcpy(addr, l2->own_addr, ETH_ALEN);
     93 	return 0;
     94 }
     95 
     96 
     97 int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
     98 		   const u8 *buf, size_t len)
     99 {
    100 	BOOL res;
    101 	DWORD written;
    102 	struct l2_ethhdr *eth;
    103 #ifndef _WIN32_WCE
    104 	OVERLAPPED overlapped;
    105 #endif /* _WIN32_WCE */
    106 	OVERLAPPED *o;
    107 
    108 	if (l2 == NULL)
    109 		return -1;
    110 
    111 #ifdef _WIN32_WCE
    112 	o = NULL;
    113 #else /* _WIN32_WCE */
    114 	os_memset(&overlapped, 0, sizeof(overlapped));
    115 	o = &overlapped;
    116 #endif /* _WIN32_WCE */
    117 
    118 	if (l2->l2_hdr) {
    119 		res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
    120 				&written, o);
    121 	} else {
    122 		size_t mlen = sizeof(*eth) + len;
    123 		eth = os_malloc(mlen);
    124 		if (eth == NULL)
    125 			return -1;
    126 
    127 		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
    128 		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
    129 		eth->h_proto = htons(proto);
    130 		os_memcpy(eth + 1, buf, len);
    131 		res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
    132 				&written, o);
    133 		os_free(eth);
    134 	}
    135 
    136 	if (!res) {
    137 		DWORD err = GetLastError();
    138 #ifndef _WIN32_WCE
    139 		if (err == ERROR_IO_PENDING) {
    140 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
    141 				   "write to complete");
    142 			res = GetOverlappedResult(
    143 				driver_ndis_get_ndisuio_handle(), &overlapped,
    144 				&written, TRUE);
    145 			if (!res) {
    146 				wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
    147 					   "GetOverlappedResult failed: %d",
    148 					   (int) GetLastError());
    149 				return -1;
    150 			}
    151 			return 0;
    152 		}
    153 #endif /* _WIN32_WCE */
    154 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
    155 			   (int) GetLastError());
    156 		return -1;
    157 	}
    158 
    159 	return 0;
    160 }
    161 
    162 
    163 static void l2_packet_callback(struct l2_packet_data *l2);
    164 
    165 #ifdef _WIN32_WCE
    166 static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
    167 {
    168 	HANDLE handles[2];
    169 
    170 	wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
    171 	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
    172 		      sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
    173 		DWORD err = GetLastError();
    174 		wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
    175 			   "%d", (int) err);
    176 		/*
    177 		 * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
    178 		 * error whenever the connection is not up. Yield the thread to
    179 		 * avoid triggering a busy loop. Connection event should stop
    180 		 * us from looping for long, but we need to allow enough CPU
    181 		 * for the main thread to process the media disconnection.
    182 		 */
    183 		Sleep(100);
    184 		return;
    185 	}
    186 
    187 	wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
    188 		   (int) l2->rx_written);
    189 
    190 	/*
    191 	 * Notify the main thread about the availability of a frame and wait
    192 	 * for the frame to be processed.
    193 	 */
    194 	SetEvent(l2->rx_avail);
    195 	handles[0] = l2_ndisuio_global->stop_request;
    196 	handles[1] = l2_ndisuio_global->rx_processed;
    197 	WaitForMultipleObjects(2, handles, FALSE, INFINITE);
    198 	ResetEvent(l2_ndisuio_global->rx_processed);
    199 }
    200 
    201 
    202 static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
    203 {
    204 	struct l2_packet_data *l2 = arg;
    205 	DWORD res;
    206 	HANDLE handles[2];
    207 	int run = 1;
    208 
    209 	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
    210 	handles[0] = l2_ndisuio_global->stop_request;
    211 	handles[1] = l2_ndisuio_global->ready_for_read;
    212 
    213 	/*
    214 	 * Unfortunately, NDISUIO on WinCE does not seem to support waiting
    215 	 * on the handle. There do not seem to be anything else that we could
    216 	 * wait for either. If one were to modify NDISUIO to set a named event
    217 	 * whenever packets are available, this event could be used here to
    218 	 * avoid having to poll for new packets or we could even move to use a
    219 	 * single threaded design.
    220 	 *
    221 	 * In addition, NDISUIO on WinCE is returning
    222 	 * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
    223 	 * the adapter is not in connected state. For now, we are just using a
    224 	 * local event to allow ReadFile calls only after having received NDIS
    225 	 * media connect event. This event could be easily converted to handle
    226 	 * another event if the protocol driver is replaced with somewhat more
    227 	 * useful design.
    228 	 */
    229 
    230 	while (l2_ndisuio_global && run) {
    231 		res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
    232 		switch (res) {
    233 		case WAIT_OBJECT_0:
    234 			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
    235 				   "request to stop RX thread");
    236 			run = 0;
    237 			break;
    238 		case WAIT_OBJECT_0 + 1:
    239 			l2_packet_rx_thread_try_read(l2);
    240 			break;
    241 		case WAIT_FAILED:
    242 		default:
    243 			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
    244 				   "WaitForMultipleObjects failed: %d",
    245 				   (int) GetLastError());
    246 			run = 0;
    247 			break;
    248 		}
    249 	}
    250 
    251 	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
    252 
    253 	return 0;
    254 }
    255 #else /* _WIN32_WCE */
    256 static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
    257 {
    258 	os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
    259 	l2->rx_overlapped.hEvent = l2->rx_avail;
    260 	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
    261 		      sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
    262 	{
    263 		DWORD err = GetLastError();
    264 		if (err != ERROR_IO_PENDING) {
    265 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
    266 				   "%d", (int) err);
    267 			return -1;
    268 		}
    269 		/*
    270 		 * Once read is completed, l2_packet_rx_event() will be
    271 		 * called.
    272 		 */
    273 	} else {
    274 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
    275 			   "without wait for completion");
    276 		if (!recursive)
    277 			l2_packet_callback(l2);
    278 	}
    279 
    280 	return 0;
    281 }
    282 #endif /* _WIN32_WCE */
    283 
    284 
    285 static void l2_packet_callback(struct l2_packet_data *l2)
    286 {
    287 	const u8 *rx_buf, *rx_src;
    288 	size_t rx_len;
    289 	struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
    290 
    291 	wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
    292 		   (int) l2->rx_written);
    293 
    294 	if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
    295 		rx_buf = (u8 *) ethhdr;
    296 		rx_len = l2->rx_written;
    297 	} else {
    298 		rx_buf = (u8 *) (ethhdr + 1);
    299 		rx_len = l2->rx_written - sizeof(*ethhdr);
    300 	}
    301 	rx_src = ethhdr->h_source;
    302 
    303 	l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
    304 #ifndef _WIN32_WCE
    305 	l2_ndisuio_start_read(l2, 1);
    306 #endif /* _WIN32_WCE */
    307 }
    308 
    309 
    310 static void l2_packet_rx_event(void *eloop_data, void *user_data)
    311 {
    312 	struct l2_packet_data *l2 = eloop_data;
    313 
    314 	if (l2_ndisuio_global)
    315 		l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
    316 
    317 	ResetEvent(l2->rx_avail);
    318 
    319 #ifndef _WIN32_WCE
    320 	if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
    321 				 &l2->rx_overlapped, &l2->rx_written, FALSE)) {
    322 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
    323 			   "failed: %d", (int) GetLastError());
    324 		return;
    325 	}
    326 #endif /* _WIN32_WCE */
    327 
    328 	l2_packet_callback(l2);
    329 
    330 #ifdef _WIN32_WCE
    331 	SetEvent(l2_ndisuio_global->rx_processed);
    332 #endif /* _WIN32_WCE */
    333 }
    334 
    335 
    336 static int l2_ndisuio_set_ether_type(unsigned short protocol)
    337 {
    338 	USHORT proto = htons(protocol);
    339 	DWORD written;
    340 
    341 	if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
    342 			     IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
    343 			     sizeof(proto), NULL, 0, &written, NULL)) {
    344 		wpa_printf(MSG_ERROR, "L2(NDISUIO): "
    345 			   "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
    346 			   (int) GetLastError());
    347 		return -1;
    348 	}
    349 
    350 	return 0;
    351 }
    352 
    353 
    354 struct l2_packet_data * l2_packet_init(
    355 	const char *ifname, const u8 *own_addr, unsigned short protocol,
    356 	void (*rx_callback)(void *ctx, const u8 *src_addr,
    357 			    const u8 *buf, size_t len),
    358 	void *rx_callback_ctx, int l2_hdr)
    359 {
    360 	struct l2_packet_data *l2;
    361 
    362 	if (l2_ndisuio_global == NULL) {
    363 		l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
    364 		if (l2_ndisuio_global == NULL)
    365 			return NULL;
    366 		l2_ndisuio_global->first_proto = protocol;
    367 	}
    368 	if (l2_ndisuio_global->refcount >= 2) {
    369 		wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
    370 			   "simultaneous connections allowed");
    371 		return NULL;
    372 	}
    373 	l2_ndisuio_global->refcount++;
    374 
    375 	l2 = os_zalloc(sizeof(struct l2_packet_data));
    376 	if (l2 == NULL)
    377 		return NULL;
    378 	l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
    379 
    380 	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
    381 	l2->rx_callback = rx_callback;
    382 	l2->rx_callback_ctx = rx_callback_ctx;
    383 	l2->l2_hdr = l2_hdr;
    384 
    385 	if (own_addr)
    386 		os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
    387 
    388 	if (l2_ndisuio_set_ether_type(protocol) < 0) {
    389 		os_free(l2);
    390 		return NULL;
    391 	}
    392 
    393 	if (l2_ndisuio_global->refcount > 1) {
    394 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
    395 			   "filtering ethertype to %04x", protocol);
    396 		if (l2_ndisuio_global->l2[0])
    397 			l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
    398 		return l2;
    399 	}
    400 
    401 	l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
    402 	if (l2->rx_avail == NULL) {
    403 		os_free(l2);
    404 		return NULL;
    405 	}
    406 
    407 	eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
    408 			     l2_packet_rx_event, l2, NULL);
    409 
    410 #ifdef _WIN32_WCE
    411 	l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
    412 	/*
    413 	 * This event is being set based on media connect/disconnect
    414 	 * notifications in driver_ndis.c.
    415 	 */
    416 	l2_ndisuio_global->ready_for_read =
    417 		CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
    418 	l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
    419 	if (l2_ndisuio_global->stop_request == NULL ||
    420 	    l2_ndisuio_global->ready_for_read == NULL ||
    421 	    l2_ndisuio_global->rx_processed == NULL) {
    422 		if (l2_ndisuio_global->stop_request) {
    423 			CloseHandle(l2_ndisuio_global->stop_request);
    424 			l2_ndisuio_global->stop_request = NULL;
    425 		}
    426 		if (l2_ndisuio_global->ready_for_read) {
    427 			CloseHandle(l2_ndisuio_global->ready_for_read);
    428 			l2_ndisuio_global->ready_for_read = NULL;
    429 		}
    430 		if (l2_ndisuio_global->rx_processed) {
    431 			CloseHandle(l2_ndisuio_global->rx_processed);
    432 			l2_ndisuio_global->rx_processed = NULL;
    433 		}
    434 		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
    435 		os_free(l2);
    436 		return NULL;
    437 	}
    438 
    439 	l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
    440 						    l2_packet_rx_thread, l2, 0,
    441 						    NULL);
    442 	if (l2_ndisuio_global->rx_thread == NULL) {
    443 		wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
    444 			   "thread: %d", (int) GetLastError());
    445 		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
    446 		CloseHandle(l2_ndisuio_global->stop_request);
    447 		l2_ndisuio_global->stop_request = NULL;
    448 		os_free(l2);
    449 		return NULL;
    450 	}
    451 #else /* _WIN32_WCE */
    452 	l2_ndisuio_start_read(l2, 0);
    453 #endif /* _WIN32_WCE */
    454 
    455 	return l2;
    456 }
    457 
    458 
    459 void l2_packet_deinit(struct l2_packet_data *l2)
    460 {
    461 	if (l2 == NULL)
    462 		return;
    463 
    464 	if (l2_ndisuio_global) {
    465 		l2_ndisuio_global->refcount--;
    466 		l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
    467 		if (l2_ndisuio_global->refcount) {
    468 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
    469 				   "ethertype to %04x",
    470 				   l2_ndisuio_global->first_proto);
    471 			l2_ndisuio_set_ether_type(
    472 				l2_ndisuio_global->first_proto);
    473 			return;
    474 		}
    475 
    476 #ifdef _WIN32_WCE
    477 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
    478 			   "stop");
    479 		SetEvent(l2_ndisuio_global->stop_request);
    480 		/*
    481 		 * Cancel pending ReadFile() in the RX thread (if we were still
    482 		 * connected at this point).
    483 		 */
    484 		if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
    485 				     IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
    486 				     NULL)) {
    487 			wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
    488 				   "failed: %d", (int) GetLastError());
    489 			/* RX thread will exit blocking ReadFile once NDISUIO
    490 			 * notices that the adapter is disconnected. */
    491 		}
    492 		WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
    493 		wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
    494 		CloseHandle(l2_ndisuio_global->rx_thread);
    495 		CloseHandle(l2_ndisuio_global->stop_request);
    496 		CloseHandle(l2_ndisuio_global->ready_for_read);
    497 		CloseHandle(l2_ndisuio_global->rx_processed);
    498 #endif /* _WIN32_WCE */
    499 
    500 		os_free(l2_ndisuio_global);
    501 		l2_ndisuio_global = NULL;
    502 	}
    503 
    504 #ifndef _WIN32_WCE
    505 	CancelIo(driver_ndis_get_ndisuio_handle());
    506 #endif /* _WIN32_WCE */
    507 
    508 	eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
    509 	CloseHandle(l2->rx_avail);
    510 	os_free(l2);
    511 }
    512 
    513 
    514 int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
    515 {
    516 	return -1;
    517 }
    518 
    519 
    520 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
    521 {
    522 }
    523