📄 l2_packet_ndis.c
字号:
/* * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING 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) { /* For now, just assume that the packet will be sent in * time before the next write happens. This could be * cleaned up at some point to actually wait for * completion before starting new writes. */ 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_WCEstatic 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){}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -