mirror of
https://github.com/vanhoefm/fragattacks.git
synced 2024-11-27 01:38:37 -05:00
517 lines
14 KiB
C
517 lines
14 KiB
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_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)
|
||
|
{
|
||
|
}
|