123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- /*
- * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
- * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- *
- * This implementation requires Windows specific event loop implementation,
- * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
- * driver_ndis.c, so only that driver interface can be used and
- * CONFIG_USE_NDISUIO must be defined.
- *
- * WinXP version of the code uses overlapped I/O and a single threaded design
- * with callback functions from I/O code. WinCE version uses a separate RX
- * thread that blocks on ReadFile() whenever the media status is connected.
- */
- #include "includes.h"
- #include <winsock2.h>
- #include <ntddndis.h>
- #ifdef _WIN32_WCE
- #include <winioctl.h>
- #include <nuiouser.h>
- #endif /* _WIN32_WCE */
- #include "common.h"
- #include "eloop.h"
- #include "l2_packet.h"
- #ifndef _WIN32_WCE
- /* from nuiouser.h */
- #define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
- #define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
- CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
- #define IOCTL_NDISUIO_SET_ETHER_TYPE \
- _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
- FILE_READ_ACCESS | FILE_WRITE_ACCESS)
- #endif /* _WIN32_WCE */
- /* From driver_ndis.c to shared the handle to NDISUIO */
- HANDLE driver_ndis_get_ndisuio_handle(void);
- /*
- * NDISUIO supports filtering of only one ethertype at the time, so we must
- * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
- * whenever wpa_supplicant is trying to pre-authenticate and then switching
- * back to EAPOL when pre-authentication has been completed.
- */
- struct l2_packet_data;
- struct l2_packet_ndisuio_global {
- int refcount;
- unsigned short first_proto;
- struct l2_packet_data *l2[2];
- #ifdef _WIN32_WCE
- HANDLE rx_thread;
- HANDLE stop_request;
- HANDLE ready_for_read;
- HANDLE rx_processed;
- #endif /* _WIN32_WCE */
- };
- static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
- struct l2_packet_data {
- char ifname[100];
- u8 own_addr[ETH_ALEN];
- void (*rx_callback)(void *ctx, const u8 *src_addr,
- const u8 *buf, size_t len);
- void *rx_callback_ctx;
- int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
- * rx_callback and l2_packet_send() */
- HANDLE rx_avail;
- #ifndef _WIN32_WCE
- OVERLAPPED rx_overlapped;
- #endif /* _WIN32_WCE */
- u8 rx_buf[1514];
- DWORD rx_written;
- };
- int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
- {
- os_memcpy(addr, l2->own_addr, ETH_ALEN);
- return 0;
- }
- int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
- const u8 *buf, size_t len)
- {
- BOOL res;
- DWORD written;
- struct l2_ethhdr *eth;
- #ifndef _WIN32_WCE
- OVERLAPPED overlapped;
- #endif /* _WIN32_WCE */
- OVERLAPPED *o;
- if (l2 == NULL)
- return -1;
- #ifdef _WIN32_WCE
- o = NULL;
- #else /* _WIN32_WCE */
- os_memset(&overlapped, 0, sizeof(overlapped));
- o = &overlapped;
- #endif /* _WIN32_WCE */
- if (l2->l2_hdr) {
- res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
- &written, o);
- } else {
- size_t mlen = sizeof(*eth) + len;
- eth = os_malloc(mlen);
- if (eth == NULL)
- return -1;
- os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
- os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
- eth->h_proto = htons(proto);
- os_memcpy(eth + 1, buf, len);
- res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
- &written, o);
- os_free(eth);
- }
- if (!res) {
- DWORD err = GetLastError();
- #ifndef _WIN32_WCE
- if (err == ERROR_IO_PENDING) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
- "write to complete");
- res = GetOverlappedResult(
- driver_ndis_get_ndisuio_handle(), &overlapped,
- &written, TRUE);
- if (!res) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
- "GetOverlappedResult failed: %d",
- (int) GetLastError());
- return -1;
- }
- return 0;
- }
- #endif /* _WIN32_WCE */
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
- (int) GetLastError());
- return -1;
- }
- return 0;
- }
- static void l2_packet_callback(struct l2_packet_data *l2);
- #ifdef _WIN32_WCE
- static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
- {
- HANDLE handles[2];
- wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
- if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
- sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
- DWORD err = GetLastError();
- wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
- "%d", (int) err);
- /*
- * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
- * error whenever the connection is not up. Yield the thread to
- * avoid triggering a busy loop. Connection event should stop
- * us from looping for long, but we need to allow enough CPU
- * for the main thread to process the media disconnection.
- */
- Sleep(100);
- return;
- }
- wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
- (int) l2->rx_written);
- /*
- * Notify the main thread about the availability of a frame and wait
- * for the frame to be processed.
- */
- SetEvent(l2->rx_avail);
- handles[0] = l2_ndisuio_global->stop_request;
- handles[1] = l2_ndisuio_global->rx_processed;
- WaitForMultipleObjects(2, handles, FALSE, INFINITE);
- ResetEvent(l2_ndisuio_global->rx_processed);
- }
- static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
- {
- struct l2_packet_data *l2 = arg;
- DWORD res;
- HANDLE handles[2];
- int run = 1;
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
- handles[0] = l2_ndisuio_global->stop_request;
- handles[1] = l2_ndisuio_global->ready_for_read;
- /*
- * Unfortunately, NDISUIO on WinCE does not seem to support waiting
- * on the handle. There do not seem to be anything else that we could
- * wait for either. If one were to modify NDISUIO to set a named event
- * whenever packets are available, this event could be used here to
- * avoid having to poll for new packets or we could even move to use a
- * single threaded design.
- *
- * In addition, NDISUIO on WinCE is returning
- * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
- * the adapter is not in connected state. For now, we are just using a
- * local event to allow ReadFile calls only after having received NDIS
- * media connect event. This event could be easily converted to handle
- * another event if the protocol driver is replaced with somewhat more
- * useful design.
- */
- while (l2_ndisuio_global && run) {
- res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
- switch (res) {
- case WAIT_OBJECT_0:
- wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
- "request to stop RX thread");
- run = 0;
- break;
- case WAIT_OBJECT_0 + 1:
- l2_packet_rx_thread_try_read(l2);
- break;
- case WAIT_FAILED:
- default:
- wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
- "WaitForMultipleObjects failed: %d",
- (int) GetLastError());
- run = 0;
- break;
- }
- }
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
- return 0;
- }
- #else /* _WIN32_WCE */
- static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
- {
- os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
- l2->rx_overlapped.hEvent = l2->rx_avail;
- if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
- sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
- {
- DWORD err = GetLastError();
- if (err != ERROR_IO_PENDING) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
- "%d", (int) err);
- return -1;
- }
- /*
- * Once read is completed, l2_packet_rx_event() will be
- * called.
- */
- } else {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
- "without wait for completion");
- if (!recursive)
- l2_packet_callback(l2);
- }
- return 0;
- }
- #endif /* _WIN32_WCE */
- static void l2_packet_callback(struct l2_packet_data *l2)
- {
- const u8 *rx_buf, *rx_src;
- size_t rx_len;
- struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
- (int) l2->rx_written);
- if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
- rx_buf = (u8 *) ethhdr;
- rx_len = l2->rx_written;
- } else {
- rx_buf = (u8 *) (ethhdr + 1);
- rx_len = l2->rx_written - sizeof(*ethhdr);
- }
- rx_src = ethhdr->h_source;
- l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
- #ifndef _WIN32_WCE
- l2_ndisuio_start_read(l2, 1);
- #endif /* _WIN32_WCE */
- }
- static void l2_packet_rx_event(void *eloop_data, void *user_data)
- {
- struct l2_packet_data *l2 = eloop_data;
- if (l2_ndisuio_global)
- l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
- ResetEvent(l2->rx_avail);
- #ifndef _WIN32_WCE
- if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
- &l2->rx_overlapped, &l2->rx_written, FALSE)) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
- "failed: %d", (int) GetLastError());
- return;
- }
- #endif /* _WIN32_WCE */
- l2_packet_callback(l2);
- #ifdef _WIN32_WCE
- SetEvent(l2_ndisuio_global->rx_processed);
- #endif /* _WIN32_WCE */
- }
- static int l2_ndisuio_set_ether_type(unsigned short protocol)
- {
- USHORT proto = htons(protocol);
- DWORD written;
- if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
- IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
- sizeof(proto), NULL, 0, &written, NULL)) {
- wpa_printf(MSG_ERROR, "L2(NDISUIO): "
- "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
- (int) GetLastError());
- return -1;
- }
- return 0;
- }
- struct l2_packet_data * 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)
- {
- struct l2_packet_data *l2;
- if (l2_ndisuio_global == NULL) {
- l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
- if (l2_ndisuio_global == NULL)
- return NULL;
- l2_ndisuio_global->first_proto = protocol;
- }
- if (l2_ndisuio_global->refcount >= 2) {
- wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
- "simultaneous connections allowed");
- return NULL;
- }
- l2_ndisuio_global->refcount++;
- l2 = os_zalloc(sizeof(struct l2_packet_data));
- if (l2 == NULL)
- return NULL;
- l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
- os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
- l2->rx_callback = rx_callback;
- l2->rx_callback_ctx = rx_callback_ctx;
- l2->l2_hdr = l2_hdr;
- if (own_addr)
- os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
- if (l2_ndisuio_set_ether_type(protocol) < 0) {
- os_free(l2);
- return NULL;
- }
- if (l2_ndisuio_global->refcount > 1) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
- "filtering ethertype to %04x", protocol);
- if (l2_ndisuio_global->l2[0])
- l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
- return l2;
- }
- l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (l2->rx_avail == NULL) {
- os_free(l2);
- return NULL;
- }
- eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
- l2_packet_rx_event, l2, NULL);
- #ifdef _WIN32_WCE
- l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
- /*
- * This event is being set based on media connect/disconnect
- * notifications in driver_ndis.c.
- */
- l2_ndisuio_global->ready_for_read =
- CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
- l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (l2_ndisuio_global->stop_request == NULL ||
- l2_ndisuio_global->ready_for_read == NULL ||
- l2_ndisuio_global->rx_processed == NULL) {
- if (l2_ndisuio_global->stop_request) {
- CloseHandle(l2_ndisuio_global->stop_request);
- l2_ndisuio_global->stop_request = NULL;
- }
- if (l2_ndisuio_global->ready_for_read) {
- CloseHandle(l2_ndisuio_global->ready_for_read);
- l2_ndisuio_global->ready_for_read = NULL;
- }
- if (l2_ndisuio_global->rx_processed) {
- CloseHandle(l2_ndisuio_global->rx_processed);
- l2_ndisuio_global->rx_processed = NULL;
- }
- eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
- os_free(l2);
- return NULL;
- }
- l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
- l2_packet_rx_thread, l2, 0,
- NULL);
- if (l2_ndisuio_global->rx_thread == NULL) {
- wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
- "thread: %d", (int) GetLastError());
- eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
- CloseHandle(l2_ndisuio_global->stop_request);
- l2_ndisuio_global->stop_request = NULL;
- os_free(l2);
- return NULL;
- }
- #else /* _WIN32_WCE */
- l2_ndisuio_start_read(l2, 0);
- #endif /* _WIN32_WCE */
- return l2;
- }
- void l2_packet_deinit(struct l2_packet_data *l2)
- {
- if (l2 == NULL)
- return;
- if (l2_ndisuio_global) {
- l2_ndisuio_global->refcount--;
- l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
- if (l2_ndisuio_global->refcount) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
- "ethertype to %04x",
- l2_ndisuio_global->first_proto);
- l2_ndisuio_set_ether_type(
- l2_ndisuio_global->first_proto);
- return;
- }
- #ifdef _WIN32_WCE
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
- "stop");
- SetEvent(l2_ndisuio_global->stop_request);
- /*
- * Cancel pending ReadFile() in the RX thread (if we were still
- * connected at this point).
- */
- if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
- IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
- NULL)) {
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
- "failed: %d", (int) GetLastError());
- /* RX thread will exit blocking ReadFile once NDISUIO
- * notices that the adapter is disconnected. */
- }
- WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
- wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
- CloseHandle(l2_ndisuio_global->rx_thread);
- CloseHandle(l2_ndisuio_global->stop_request);
- CloseHandle(l2_ndisuio_global->ready_for_read);
- CloseHandle(l2_ndisuio_global->rx_processed);
- #endif /* _WIN32_WCE */
- os_free(l2_ndisuio_global);
- l2_ndisuio_global = NULL;
- }
- #ifndef _WIN32_WCE
- CancelIo(driver_ndis_get_ndisuio_handle());
- #endif /* _WIN32_WCE */
- eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
- CloseHandle(l2->rx_avail);
- os_free(l2);
- }
- int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
- {
- return -1;
- }
- void l2_packet_notify_auth_start(struct l2_packet_data *l2)
- {
- }
- int l2_packet_set_packet_filter(struct l2_packet_data *l2,
- enum l2_packet_filter_type type)
- {
- return -1;
- }
|