2
* WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
3
* Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
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.
9
* Alternatively, this software may be distributed under the terms of BSD
12
* See README and COPYING for more details.
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.
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.
31
#endif /* _WIN32_WCE */
35
#include "l2_packet.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 */
47
/* From driver_ndis.c to shared the handle to NDISUIO */
48
HANDLE driver_ndis_get_ndisuio_handle(void);
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.
57
struct l2_packet_data;
59
struct l2_packet_ndisuio_global {
61
unsigned short first_proto;
62
struct l2_packet_data *l2[2];
66
HANDLE ready_for_read;
68
#endif /* _WIN32_WCE */
71
static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
73
struct l2_packet_data {
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() */
83
OVERLAPPED rx_overlapped;
84
#endif /* _WIN32_WCE */
90
int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
92
os_memcpy(addr, l2->own_addr, ETH_ALEN);
97
int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
98
const u8 *buf, size_t len)
102
struct l2_ethhdr *eth;
104
OVERLAPPED overlapped;
105
#endif /* _WIN32_WCE */
113
#else /* _WIN32_WCE */
114
os_memset(&overlapped, 0, sizeof(overlapped));
116
#endif /* _WIN32_WCE */
119
res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
122
size_t mlen = sizeof(*eth) + len;
123
eth = os_malloc(mlen);
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,
137
DWORD err = GetLastError();
139
if (err == ERROR_IO_PENDING) {
140
/* For now, just assume that the packet will be sent in
141
* time before the next write happens. This could be
142
* cleaned up at some point to actually wait for
143
* completion before starting new writes.
147
#endif /* _WIN32_WCE */
148
wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
149
(int) GetLastError());
157
static void l2_packet_callback(struct l2_packet_data *l2);
160
static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
164
wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
165
if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
166
sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
167
DWORD err = GetLastError();
168
wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
171
* ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
172
* error whenever the connection is not up. Yield the thread to
173
* avoid triggering a busy loop. Connection event should stop
174
* us from looping for long, but we need to allow enough CPU
175
* for the main thread to process the media disconnection.
181
wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
182
(int) l2->rx_written);
185
* Notify the main thread about the availability of a frame and wait
186
* for the frame to be processed.
188
SetEvent(l2->rx_avail);
189
handles[0] = l2_ndisuio_global->stop_request;
190
handles[1] = l2_ndisuio_global->rx_processed;
191
WaitForMultipleObjects(2, handles, FALSE, INFINITE);
192
ResetEvent(l2_ndisuio_global->rx_processed);
196
static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
198
struct l2_packet_data *l2 = arg;
203
wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
204
handles[0] = l2_ndisuio_global->stop_request;
205
handles[1] = l2_ndisuio_global->ready_for_read;
208
* Unfortunately, NDISUIO on WinCE does not seem to support waiting
209
* on the handle. There do not seem to be anything else that we could
210
* wait for either. If one were to modify NDISUIO to set a named event
211
* whenever packets are available, this event could be used here to
212
* avoid having to poll for new packets or we could even move to use a
213
* single threaded design.
215
* In addition, NDISUIO on WinCE is returning
216
* ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
217
* the adapter is not in connected state. For now, we are just using a
218
* local event to allow ReadFile calls only after having received NDIS
219
* media connect event. This event could be easily converted to handle
220
* another event if the protocol driver is replaced with somewhat more
224
while (l2_ndisuio_global && run) {
225
res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
228
wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
229
"request to stop RX thread");
232
case WAIT_OBJECT_0 + 1:
233
l2_packet_rx_thread_try_read(l2);
237
wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
238
"WaitForMultipleObjects failed: %d",
239
(int) GetLastError());
245
wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
249
#else /* _WIN32_WCE */
250
static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
252
os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
253
l2->rx_overlapped.hEvent = l2->rx_avail;
254
if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
255
sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
257
DWORD err = GetLastError();
258
if (err != ERROR_IO_PENDING) {
259
wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
264
* Once read is completed, l2_packet_rx_event() will be
268
wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
269
"without wait for completion");
271
l2_packet_callback(l2);
276
#endif /* _WIN32_WCE */
279
static void l2_packet_callback(struct l2_packet_data *l2)
281
const u8 *rx_buf, *rx_src;
283
struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
285
wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
286
(int) l2->rx_written);
288
if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
289
rx_buf = (u8 *) ethhdr;
290
rx_len = l2->rx_written;
292
rx_buf = (u8 *) (ethhdr + 1);
293
rx_len = l2->rx_written - sizeof(*ethhdr);
295
rx_src = ethhdr->h_source;
297
l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
299
l2_ndisuio_start_read(l2, 1);
300
#endif /* _WIN32_WCE */
304
static void l2_packet_rx_event(void *eloop_data, void *user_data)
306
struct l2_packet_data *l2 = eloop_data;
308
if (l2_ndisuio_global)
309
l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
311
ResetEvent(l2->rx_avail);
314
if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
315
&l2->rx_overlapped, &l2->rx_written, FALSE)) {
316
wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
317
"failed: %d", (int) GetLastError());
320
#endif /* _WIN32_WCE */
322
l2_packet_callback(l2);
325
SetEvent(l2_ndisuio_global->rx_processed);
326
#endif /* _WIN32_WCE */
330
static int l2_ndisuio_set_ether_type(unsigned short protocol)
332
USHORT proto = htons(protocol);
335
if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
336
IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
337
sizeof(proto), NULL, 0, &written, NULL)) {
338
wpa_printf(MSG_ERROR, "L2(NDISUIO): "
339
"IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
340
(int) GetLastError());
348
struct l2_packet_data * l2_packet_init(
349
const char *ifname, const u8 *own_addr, unsigned short protocol,
350
void (*rx_callback)(void *ctx, const u8 *src_addr,
351
const u8 *buf, size_t len),
352
void *rx_callback_ctx, int l2_hdr)
354
struct l2_packet_data *l2;
356
if (l2_ndisuio_global == NULL) {
357
l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
358
if (l2_ndisuio_global == NULL)
360
l2_ndisuio_global->first_proto = protocol;
362
if (l2_ndisuio_global->refcount >= 2) {
363
wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
364
"simultaneous connections allowed");
367
l2_ndisuio_global->refcount++;
369
l2 = os_zalloc(sizeof(struct l2_packet_data));
372
l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
374
os_strncpy(l2->ifname, ifname, sizeof(l2->ifname));
375
l2->rx_callback = rx_callback;
376
l2->rx_callback_ctx = rx_callback_ctx;
380
os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
382
if (l2_ndisuio_set_ether_type(protocol) < 0) {
387
if (l2_ndisuio_global->refcount > 1) {
388
wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
389
"filtering ethertype to %04x", protocol);
390
if (l2_ndisuio_global->l2[0])
391
l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
395
l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
396
if (l2->rx_avail == NULL) {
401
eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
402
l2_packet_rx_event, l2, NULL);
405
l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
407
* This event is being set based on media connect/disconnect
408
* notifications in driver_ndis.c.
410
l2_ndisuio_global->ready_for_read =
411
CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
412
l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
413
if (l2_ndisuio_global->stop_request == NULL ||
414
l2_ndisuio_global->ready_for_read == NULL ||
415
l2_ndisuio_global->rx_processed == NULL) {
416
if (l2_ndisuio_global->stop_request) {
417
CloseHandle(l2_ndisuio_global->stop_request);
418
l2_ndisuio_global->stop_request = NULL;
420
if (l2_ndisuio_global->ready_for_read) {
421
CloseHandle(l2_ndisuio_global->ready_for_read);
422
l2_ndisuio_global->ready_for_read = NULL;
424
if (l2_ndisuio_global->rx_processed) {
425
CloseHandle(l2_ndisuio_global->rx_processed);
426
l2_ndisuio_global->rx_processed = NULL;
428
eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
433
l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
434
l2_packet_rx_thread, l2, 0,
436
if (l2_ndisuio_global->rx_thread == NULL) {
437
wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
438
"thread: %d", (int) GetLastError());
439
eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
440
CloseHandle(l2_ndisuio_global->stop_request);
441
l2_ndisuio_global->stop_request = NULL;
445
#else /* _WIN32_WCE */
446
l2_ndisuio_start_read(l2, 0);
447
#endif /* _WIN32_WCE */
453
void l2_packet_deinit(struct l2_packet_data *l2)
458
if (l2_ndisuio_global) {
459
l2_ndisuio_global->refcount--;
460
l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
461
if (l2_ndisuio_global->refcount) {
462
wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
464
l2_ndisuio_global->first_proto);
465
l2_ndisuio_set_ether_type(
466
l2_ndisuio_global->first_proto);
471
wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
473
SetEvent(l2_ndisuio_global->stop_request);
475
* Cancel pending ReadFile() in the RX thread (if we were still
476
* connected at this point).
478
if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
479
IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
481
wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
482
"failed: %d", (int) GetLastError());
483
/* RX thread will exit blocking ReadFile once NDISUIO
484
* notices that the adapter is disconnected. */
486
WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
487
wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
488
CloseHandle(l2_ndisuio_global->rx_thread);
489
CloseHandle(l2_ndisuio_global->stop_request);
490
CloseHandle(l2_ndisuio_global->ready_for_read);
491
CloseHandle(l2_ndisuio_global->rx_processed);
492
#endif /* _WIN32_WCE */
494
os_free(l2_ndisuio_global);
495
l2_ndisuio_global = NULL;
499
CancelIo(driver_ndis_get_ndisuio_handle());
500
#endif /* _WIN32_WCE */
502
eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
503
CloseHandle(l2->rx_avail);
508
int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
514
void l2_packet_notify_auth_start(struct l2_packet_data *l2)