hostapd: Update vendor branch to 0.6.10
[dragonfly.git] / contrib / hostapd / src / l2_packet / l2_packet_ndis.c
blob7de58808d63c506cbaea1532e7e423d47daf1997
1 /*
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
10 * license.
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.
24 #include "includes.h"
25 #include <winsock2.h>
26 #include <ntddndis.h>
28 #ifdef _WIN32_WCE
29 #include <winioctl.h>
30 #include <nuiouser.h>
31 #endif /* _WIN32_WCE */
33 #include "common.h"
34 #include "eloop.h"
35 #include "l2_packet.h"
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 */
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 {
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 */
71 static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
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;
90 int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
92 os_memcpy(addr, l2->own_addr, ETH_ALEN);
93 return 0;
97 int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
98 const u8 *buf, size_t len)
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;
108 if (l2 == NULL)
109 return -1;
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 */
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;
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);
136 if (!res) {
137 DWORD err = GetLastError();
138 #ifndef _WIN32_WCE
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.
145 return 0;
147 #endif /* _WIN32_WCE */
148 wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
149 (int) GetLastError());
150 return -1;
153 return 0;
157 static void l2_packet_callback(struct l2_packet_data *l2);
159 #ifdef _WIN32_WCE
160 static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
162 HANDLE handles[2];
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: "
169 "%d", (int) err);
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.
177 Sleep(100);
178 return;
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;
199 DWORD res;
200 HANDLE handles[2];
201 int run = 1;
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
221 * useful design.
224 while (l2_ndisuio_global && run) {
225 res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
226 switch (res) {
227 case WAIT_OBJECT_0:
228 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
229 "request to stop RX thread");
230 run = 0;
231 break;
232 case WAIT_OBJECT_0 + 1:
233 l2_packet_rx_thread_try_read(l2);
234 break;
235 case WAIT_FAILED:
236 default:
237 wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
238 "WaitForMultipleObjects failed: %d",
239 (int) GetLastError());
240 run = 0;
241 break;
245 wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
247 return 0;
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: "
260 "%d", (int) err);
261 return -1;
264 * Once read is completed, l2_packet_rx_event() will be
265 * called.
267 } else {
268 wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
269 "without wait for completion");
270 if (!recursive)
271 l2_packet_callback(l2);
274 return 0;
276 #endif /* _WIN32_WCE */
279 static void l2_packet_callback(struct l2_packet_data *l2)
281 const u8 *rx_buf, *rx_src;
282 size_t rx_len;
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;
291 } else {
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);
298 #ifndef _WIN32_WCE
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);
313 #ifndef _WIN32_WCE
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());
318 return;
320 #endif /* _WIN32_WCE */
322 l2_packet_callback(l2);
324 #ifdef _WIN32_WCE
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);
333 DWORD written;
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());
341 return -1;
344 return 0;
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)
359 return 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");
365 return NULL;
367 l2_ndisuio_global->refcount++;
369 l2 = os_zalloc(sizeof(struct l2_packet_data));
370 if (l2 == NULL)
371 return NULL;
372 l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
374 os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
375 l2->rx_callback = rx_callback;
376 l2->rx_callback_ctx = rx_callback_ctx;
377 l2->l2_hdr = l2_hdr;
379 if (own_addr)
380 os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
382 if (l2_ndisuio_set_ether_type(protocol) < 0) {
383 os_free(l2);
384 return NULL;
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;
392 return l2;
395 l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
396 if (l2->rx_avail == NULL) {
397 os_free(l2);
398 return NULL;
401 eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
402 l2_packet_rx_event, l2, NULL);
404 #ifdef _WIN32_WCE
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));
429 os_free(l2);
430 return NULL;
433 l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
434 l2_packet_rx_thread, l2, 0,
435 NULL);
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;
442 os_free(l2);
443 return NULL;
445 #else /* _WIN32_WCE */
446 l2_ndisuio_start_read(l2, 0);
447 #endif /* _WIN32_WCE */
449 return l2;
453 void l2_packet_deinit(struct l2_packet_data *l2)
455 if (l2 == NULL)
456 return;
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 "
463 "ethertype to %04x",
464 l2_ndisuio_global->first_proto);
465 l2_ndisuio_set_ether_type(
466 l2_ndisuio_global->first_proto);
467 return;
470 #ifdef _WIN32_WCE
471 wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
472 "stop");
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,
480 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;
498 #ifndef _WIN32_WCE
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);
504 os_free(l2);
508 int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
510 return -1;
514 void l2_packet_notify_auth_start(struct l2_packet_data *l2)