1102 lines
31 KiB
C
1102 lines
31 KiB
C
/*********************************************************************
|
|
PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
|
|
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
|
|
|
Authors: Maxime Vincent
|
|
Based on the OpenVPN tun.c driver, under GPL
|
|
|
|
NOTES: This is the Windows-only driver, a Linux-equivalent is available, too
|
|
You need to have an OpenVPN TUN/TAP network adapter installed, first
|
|
This driver is barely working:
|
|
* Only TAP-mode is supported (TUN is not)
|
|
* it will simply open the first TAP device it can find
|
|
* there is memory being allocated that's never freed
|
|
* there is no destroy function, yet
|
|
* it has only been tested on a Windows 7 machine
|
|
*********************************************************************/
|
|
|
|
#include "pico_device.h"
|
|
#include "pico_dev_null.h"
|
|
#include "pico_stack.h"
|
|
#include "pico_dev_tap_windows.h"
|
|
|
|
#include <Windows.h>
|
|
#include <Winreg.h>
|
|
#include <winioctl.h>
|
|
#include "pico_dev_tap_windows_private.h"
|
|
|
|
/*
|
|
* Debugging info
|
|
*/
|
|
#ifdef DEBUG_TAP_GENERAL
|
|
#define dbg_tap dbg /* first level debug */
|
|
#else
|
|
#define dbg_tap(...) do{} while(0)
|
|
#endif
|
|
|
|
#ifdef DEBUG_TAP_INFO
|
|
#define dbg_tap_info dbg /* tap info messages */
|
|
#else
|
|
#define dbg_tap_info(...) do{} while(0)
|
|
#endif
|
|
|
|
#ifdef DEBUG_TAP_WIN
|
|
#define dbg_tap_win32 dbg /* second level detailed win32 debug */
|
|
#else
|
|
#define dbg_tap_win32(...) do{} while(0)
|
|
#endif
|
|
|
|
#ifdef DEBUG_TAP_REG
|
|
#define dbg_tap_reg dbg /* third level: registry debug */
|
|
#else
|
|
#define dbg_tap_reg(...) do{} while(0)
|
|
#endif
|
|
|
|
/*
|
|
* Tunnel types
|
|
*/
|
|
#define DEV_TYPE_UNDEF 0
|
|
#define DEV_TYPE_NULL 1
|
|
#define DEV_TYPE_TUN 2 /* point-to-point IP tunnel */
|
|
#define DEV_TYPE_TAP 3 /* ethernet (802.3) tunnel */
|
|
|
|
|
|
/*
|
|
* We try to do all Win32 I/O using overlapped
|
|
* (i.e. asynchronous) I/O for a performance win.
|
|
*/
|
|
struct overlapped_io {
|
|
# define IOSTATE_INITIAL 0
|
|
# define IOSTATE_QUEUED 1 /* overlapped I/O has been queued */
|
|
# define IOSTATE_IMMEDIATE_RETURN 2 /* I/O function returned immediately without queueing */
|
|
int iostate;
|
|
OVERLAPPED overlapped;
|
|
DWORD size;
|
|
DWORD flags;
|
|
int status;
|
|
int addr_defined;
|
|
uint8_t *buf_init;
|
|
uint32_t buf_init_len;
|
|
uint8_t *buf;
|
|
uint32_t buf_len;
|
|
};
|
|
|
|
struct rw_handle {
|
|
HANDLE read;
|
|
HANDLE write;
|
|
};
|
|
|
|
struct tuntap
|
|
{
|
|
int type; /* DEV_TYPE_x as defined in proto.h */
|
|
int ipv6;
|
|
int persistent_if; /* if existed before, keep on program end */
|
|
char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */
|
|
int post_open_mtu;
|
|
uint8_t mac[6];
|
|
|
|
/* Windows stuff */
|
|
DWORD adapter_index; /*adapter index for TAP-Windows adapter, ~0 if undefined */
|
|
HANDLE hand;
|
|
struct overlapped_io reads; /* for overlapped IO */
|
|
struct overlapped_io writes;
|
|
struct rw_handle rw_handle;
|
|
|
|
};
|
|
|
|
|
|
struct pico_device_tap {
|
|
struct pico_device dev;
|
|
int statistics_frames_out;
|
|
struct tuntap *tt;
|
|
};
|
|
|
|
|
|
/*
|
|
* Private function prototypes
|
|
*/
|
|
const struct tap_reg *get_tap_reg (void);
|
|
const struct panel_reg *get_panel_reg (void);
|
|
|
|
|
|
/*
|
|
* Private functions
|
|
*/
|
|
|
|
/* Get TAP info from Windows registry */
|
|
const struct tap_reg *get_tap_reg (void)
|
|
{
|
|
HKEY adapter_key;
|
|
LONG status;
|
|
DWORD len;
|
|
struct tap_reg *first = NULL;
|
|
struct tap_reg *last = NULL;
|
|
int i = 0;
|
|
|
|
status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
ADAPTER_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&adapter_key);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
dbg_tap_reg("Error opening registry key: %s\n", ADAPTER_KEY);
|
|
return NULL;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
char enum_name[256];
|
|
char unit_string[256];
|
|
HKEY unit_key;
|
|
char component_id_string[] = "ComponentId";
|
|
char component_id[256];
|
|
char net_cfg_instance_id_string[] = "NetCfgInstanceId";
|
|
char net_cfg_instance_id[256];
|
|
DWORD data_type;
|
|
|
|
len = sizeof (enum_name);
|
|
status = RegEnumKeyEx(
|
|
adapter_key,
|
|
i,
|
|
enum_name,
|
|
&len,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (status == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
else if (status != ERROR_SUCCESS)
|
|
dbg_tap_reg("Error enumerating registry subkeys of key: %s.\n", ADAPTER_KEY);
|
|
|
|
snprintf (unit_string, sizeof(unit_string), "%s\\%s",
|
|
ADAPTER_KEY, enum_name);
|
|
|
|
status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
unit_string,
|
|
0,
|
|
KEY_READ,
|
|
&unit_key);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
dbg_tap_reg("Error opening registry key: %s\n", unit_string);
|
|
}
|
|
else
|
|
{
|
|
len = sizeof (component_id);
|
|
status = RegQueryValueEx(
|
|
unit_key,
|
|
component_id_string,
|
|
NULL,
|
|
&data_type,
|
|
(LPBYTE)component_id,
|
|
&len);
|
|
|
|
if (status != ERROR_SUCCESS || data_type != REG_SZ)
|
|
{
|
|
dbg_tap_reg("Error opening registry key: %s\\%s\n", unit_string, component_id_string);
|
|
}
|
|
else
|
|
{
|
|
len = sizeof (net_cfg_instance_id);
|
|
status = RegQueryValueEx(
|
|
unit_key,
|
|
net_cfg_instance_id_string,
|
|
NULL,
|
|
&data_type,
|
|
(LPBYTE)net_cfg_instance_id,
|
|
&len);
|
|
|
|
if (status == ERROR_SUCCESS && data_type == REG_SZ)
|
|
{
|
|
if (!strcmp (component_id, TAP_WIN_COMPONENT_ID))
|
|
{
|
|
struct tap_reg *reg;
|
|
reg = PICO_ZALLOC(sizeof(struct tap_reg), 1);
|
|
/* ALLOC_OBJ_CLEAR_GC (reg, struct tap_reg, gc); */
|
|
if (!reg)
|
|
return NULL;
|
|
|
|
/* reg->guid = string_alloc (net_cfg_instance_id, gc); */
|
|
reg->guid = PICO_ZALLOC (strlen(net_cfg_instance_id) + 1, 1);
|
|
if (!(reg->guid))
|
|
{
|
|
PICO_FREE(reg);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy((char *)reg->guid, net_cfg_instance_id);
|
|
/* link into return list */
|
|
if (!first)
|
|
first = reg;
|
|
|
|
if (last)
|
|
last->next = reg;
|
|
|
|
last = reg;
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey (unit_key);
|
|
}
|
|
|
|
++i;
|
|
}
|
|
RegCloseKey (adapter_key);
|
|
return first;
|
|
}
|
|
|
|
/* Get Panel info from Windows registry */
|
|
const struct panel_reg *get_panel_reg (void)
|
|
{
|
|
LONG status;
|
|
HKEY network_connections_key;
|
|
DWORD len;
|
|
struct panel_reg *first = NULL;
|
|
struct panel_reg *last = NULL;
|
|
int i = 0;
|
|
|
|
status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
NETWORK_CONNECTIONS_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&network_connections_key);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
dbg_tap_reg("Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
|
|
return NULL;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
char enum_name[256];
|
|
char connection_string[256];
|
|
HKEY connection_key;
|
|
WCHAR name_data[256];
|
|
DWORD name_type;
|
|
const WCHAR name_string[] = L"Name";
|
|
|
|
len = sizeof (enum_name);
|
|
status = RegEnumKeyEx(
|
|
network_connections_key,
|
|
i,
|
|
enum_name,
|
|
&len,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (status == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
else if (status != ERROR_SUCCESS)
|
|
dbg_tap_reg("Error enumerating registry subkeys of key: %s.\n", NETWORK_CONNECTIONS_KEY);
|
|
|
|
snprintf (connection_string, sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name);
|
|
|
|
status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
connection_string,
|
|
0,
|
|
KEY_READ,
|
|
&connection_key);
|
|
if (status != ERROR_SUCCESS)
|
|
dbg_tap_reg("Error opening registry key: %s\n", connection_string);
|
|
else
|
|
{
|
|
len = sizeof (name_data);
|
|
status = RegQueryValueExW(
|
|
connection_key,
|
|
name_string,
|
|
NULL,
|
|
&name_type,
|
|
(LPBYTE) name_data,
|
|
&len);
|
|
|
|
if (status != ERROR_SUCCESS || name_type != REG_SZ)
|
|
dbg_tap_reg("Error opening registry key: %s\\%s\\%S\n", NETWORK_CONNECTIONS_KEY, connection_string, name_string);
|
|
else
|
|
{
|
|
int n;
|
|
LPSTR name;
|
|
struct panel_reg *reg;
|
|
|
|
/* ALLOC_OBJ_CLEAR_GC (reg, struct panel_reg, gc); */
|
|
reg = PICO_ZALLOC(sizeof(struct panel_reg), 1);
|
|
if (!reg)
|
|
return NULL;
|
|
|
|
n = WideCharToMultiByte (CP_UTF8, 0, name_data, -1, NULL, 0, NULL, NULL);
|
|
/* name = gc_malloc (n, false, gc); */
|
|
name = PICO_ZALLOC(n, 1);
|
|
if (!name)
|
|
{
|
|
PICO_FREE(reg);
|
|
return NULL;
|
|
}
|
|
|
|
WideCharToMultiByte (CP_UTF8, 0, name_data, -1, name, n, NULL, NULL);
|
|
reg->name = name;
|
|
/* reg->guid = string_alloc (enum_name, gc); */
|
|
reg->guid = PICO_ZALLOC(strlen(enum_name) + 1, 1);
|
|
if (!reg->guid)
|
|
{
|
|
PICO_FREE((void *)reg->name);
|
|
PICO_FREE((void *)reg);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy((char *)reg->guid, enum_name);
|
|
|
|
/* link into return list */
|
|
if (!first)
|
|
first = reg;
|
|
|
|
if (last)
|
|
last->next = reg;
|
|
|
|
last = reg;
|
|
}
|
|
|
|
RegCloseKey (connection_key);
|
|
}
|
|
|
|
++i;
|
|
}
|
|
RegCloseKey (network_connections_key);
|
|
|
|
return first;
|
|
}
|
|
|
|
|
|
void show_tap_win_adapters (void)
|
|
{
|
|
int warn_panel_null = 0;
|
|
int warn_panel_dup = 0;
|
|
int warn_tap_dup = 0;
|
|
|
|
int links;
|
|
|
|
const struct tap_reg *tr;
|
|
const struct tap_reg *tr1;
|
|
const struct panel_reg *pr;
|
|
|
|
const struct tap_reg *tap_reg = get_tap_reg ();
|
|
const struct panel_reg *panel_reg = get_panel_reg ();
|
|
|
|
if (!(tap_reg && panel_reg))
|
|
return;
|
|
|
|
dbg_tap_info("Available TAP-WIN32 adapters [name, GUID]:\n");
|
|
|
|
/* loop through each TAP-Windows adapter registry entry */
|
|
for (tr = tap_reg; tr != NULL; tr = tr->next)
|
|
{
|
|
links = 0;
|
|
|
|
/* loop through each network connections entry in the control panel */
|
|
for (pr = panel_reg; pr != NULL; pr = pr->next)
|
|
{
|
|
if (!strcmp (tr->guid, pr->guid))
|
|
{
|
|
dbg_tap_info("\t>> '%s' %s\n", pr->name, tr->guid);
|
|
++links;
|
|
}
|
|
}
|
|
if (links > 1)
|
|
{
|
|
warn_panel_dup = 1;
|
|
}
|
|
else if (links == 0)
|
|
{
|
|
/* a TAP adapter exists without a link from the network
|
|
connections control panel */
|
|
warn_panel_null = 1;
|
|
dbg_tap_info("\t>> [NULL] %s\n", tr->guid);
|
|
}
|
|
}
|
|
/* check for TAP-Windows adapter duplicated GUIDs */
|
|
for (tr = tap_reg; tr != NULL; tr = tr->next)
|
|
{
|
|
for (tr1 = tap_reg; tr1 != NULL; tr1 = tr1->next)
|
|
{
|
|
if (tr != tr1 && !strcmp (tr->guid, tr1->guid))
|
|
warn_tap_dup = 1;
|
|
}
|
|
}
|
|
/* warn on registry inconsistencies */
|
|
if (warn_tap_dup)
|
|
dbg_tap_info("WARNING: Some TAP-Windows adapters have duplicate GUIDs\n");
|
|
|
|
if (warn_panel_dup)
|
|
dbg_tap_info("WARNING: Some TAP-Windows adapters have duplicate links from the Network Connections control panel\n");
|
|
|
|
if (warn_panel_null)
|
|
dbg_tap_info("WARNING: Some TAP-Windows adapters have no link from the Network Connections control panel\n");
|
|
}
|
|
|
|
|
|
/* Get the GUID of the first TAP device found */
|
|
const char *get_first_device_guid(const struct tap_reg *tap_reg, const struct panel_reg *panel_reg, char *name)
|
|
{
|
|
const struct tap_reg *tr;
|
|
const struct panel_reg *pr;
|
|
/* loop through each TAP-Windows adapter registry entry */
|
|
for (tr = tap_reg; tr != NULL; tr = tr->next)
|
|
{
|
|
/* loop through each network connections entry in the control panel */
|
|
for (pr = panel_reg; pr != NULL; pr = pr->next)
|
|
{
|
|
if (!strcmp (tr->guid, pr->guid))
|
|
{
|
|
dbg_tap_info("Using first TAP device: '%s' %s\n", pr->name, tr->guid);
|
|
if (name)
|
|
strcpy(name, pr->name);
|
|
|
|
return tr->guid;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
int open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
|
|
{
|
|
char device_path[256];
|
|
const char *device_guid = NULL;
|
|
DWORD len;
|
|
|
|
dbg_tap_info("open_tun, tt->ipv6=%d\n", tt->ipv6 );
|
|
|
|
if (!(tt->type == DEV_TYPE_TAP || tt->type == DEV_TYPE_TUN))
|
|
{
|
|
dbg_tap_info("Unknown virtual device type: '%s'\n", dev);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Lookup the device name in the registry, using the --dev-node high level name.
|
|
*/
|
|
{
|
|
const struct tap_reg *tap_reg = get_tap_reg();
|
|
const struct panel_reg *panel_reg = get_panel_reg();
|
|
char name[256];
|
|
|
|
if (!(tap_reg && panel_reg))
|
|
{
|
|
dbg_tap_info("No TUN/TAP devices found\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Get the device GUID for the device specified with --dev-node. */
|
|
device_guid = get_first_device_guid (tap_reg, panel_reg, name);
|
|
|
|
if (!device_guid)
|
|
dbg_tap_info("TAP-Windows adapter '%s' not found\n", dev_node);
|
|
|
|
/* Open Windows TAP-Windows adapter */
|
|
snprintf (device_path, sizeof(device_path), "%s%s%s",
|
|
USERMODEDEVICEDIR,
|
|
device_guid,
|
|
TAP_WIN_SUFFIX);
|
|
|
|
tt->hand = CreateFile (
|
|
device_path,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, /* was: FILE_SHARE_READ */
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
|
|
0
|
|
);
|
|
|
|
if (tt->hand == INVALID_HANDLE_VALUE)
|
|
dbg_tap_info("CreateFile failed on TAP device: %s\n", device_path);
|
|
|
|
/* translate high-level device name into a device instance
|
|
GUID using the registry */
|
|
tt->actual_name = PICO_ZALLOC(strlen(name) + 1);
|
|
if (tt->actual_name)
|
|
strcpy(tt->actual_name, name);
|
|
}
|
|
|
|
dbg_tap_info("TAP-WIN32 device [%s] opened: %s\n", tt->actual_name, device_path);
|
|
/* TODO TODO TODO */
|
|
/* tt->adapter_index = get_adapter_index (device_guid); */
|
|
|
|
/* get driver version info */
|
|
{
|
|
ULONG info[3];
|
|
/* TODO TODO TODO */
|
|
/* CLEAR (info); */
|
|
if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_VERSION, &info, sizeof (info), &info, sizeof (info), &len, NULL))
|
|
{
|
|
dbg_tap_info ("TAP-Windows Driver Version %d.%d %s\n",
|
|
(int) info[0],
|
|
(int) info[1],
|
|
(info[2] ? "(DEBUG)" : ""));
|
|
|
|
}
|
|
|
|
if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR))
|
|
dbg_tap_info ("ERROR: This version of " PACKAGE_NAME " requires a TAP-Windows driver that is at least version %d.%d \
|
|
-- If you recently upgraded your " PACKAGE_NAME " distribution, \
|
|
a reboot is probably required at this point to get Windows to see the new driver.\n",
|
|
TAP_WIN_MIN_MAJOR,
|
|
TAP_WIN_MIN_MINOR);
|
|
|
|
/* usage of numeric constants is ugly, but this is really tied to
|
|
* *this* version of the driver
|
|
*/
|
|
if ( tt->ipv6 && tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] < 8)
|
|
{
|
|
dbg_tap_info("WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will be disabled. \
|
|
Upgrade to Tap-Win32 9.8 (2.2-beta3 release or later) or use TAP mode to get IPv6\n", (int) info[0], (int) info[1] );
|
|
tt->ipv6 = 0;
|
|
}
|
|
|
|
/* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy
|
|
*/
|
|
if ( tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] == 8)
|
|
{
|
|
dbg_tap_info("ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade to Tap-Win32 9.9 (2.2.2 release or later) or use TAP mode\n", (int) info[0], (int) info[1] );
|
|
}
|
|
}
|
|
|
|
/* get driver MTU */
|
|
{
|
|
ULONG mtu;
|
|
if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_MTU,
|
|
&mtu, sizeof (mtu),
|
|
&mtu, sizeof (mtu), &len, NULL))
|
|
{
|
|
tt->post_open_mtu = (int) mtu;
|
|
dbg_tap_info("TAP-Windows MTU=%d\n", (int) mtu);
|
|
}
|
|
}
|
|
|
|
|
|
/* get driver MAC */
|
|
{
|
|
uint8_t mac[6] = {
|
|
0, 0, 0, 0, 0, 0
|
|
};
|
|
if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_MAC,
|
|
mac, sizeof (mac),
|
|
mac, sizeof (mac), &len, NULL))
|
|
{
|
|
dbg_tap_info("TAP-Windows MAC=[%x,%x,%x,%x,%x,%x]\n", mac[0], mac[1], mac[2],
|
|
mac[2], mac[4], mac[5]);
|
|
memcpy(tt->mac, mac, sizeof(mac));
|
|
}
|
|
}
|
|
|
|
/* set point-to-point mode if TUN device */
|
|
|
|
if (tt->type == DEV_TYPE_TUN)
|
|
{
|
|
dbg_tap_info("TUN type not supported for now...\n");
|
|
return -1;
|
|
}
|
|
else if (tt->type == DEV_TYPE_TAP)
|
|
{ /* TAP DEVICE */
|
|
dbg_tap_info("TODO: Set Point-to-point through DeviceIoControl\n");
|
|
}
|
|
|
|
/* set driver media status to 'connected' */
|
|
{
|
|
ULONG status = TRUE;
|
|
if (!DeviceIoControl (tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
|
|
&status, sizeof (status),
|
|
&status, sizeof (status), &len, NULL))
|
|
dbg_tap_info("WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: Closing a TUN device is currently not implemented */
|
|
/*
|
|
void close_tun (struct tuntap *tt)
|
|
{
|
|
(void)tt;
|
|
}
|
|
*/
|
|
|
|
|
|
int tap_win_getinfo (const struct tuntap *tt, char *buf, int bufsize)
|
|
{
|
|
if (tt && tt->hand != NULL && buf != NULL)
|
|
{
|
|
DWORD len;
|
|
if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_INFO,
|
|
buf, bufsize,
|
|
buf, bufsize,
|
|
&len, NULL))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void tun_show_debug (struct tuntap *tt, char *buf, int bufsize)
|
|
{
|
|
if (tt && tt->hand != NULL && buf != NULL)
|
|
{
|
|
DWORD len;
|
|
while (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_LOG_LINE,
|
|
buf, bufsize,
|
|
buf, bufsize,
|
|
&len, NULL))
|
|
{
|
|
dbg_tap_info("TAP-Windows: %s\n", buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* returns the state */
|
|
int tun_read_queue (struct tuntap *tt, uint8_t *buffer, int maxsize)
|
|
{
|
|
if (tt->reads.iostate == IOSTATE_INITIAL)
|
|
{
|
|
DWORD len = 1500;
|
|
BOOL status;
|
|
int err;
|
|
|
|
/* reset buf to its initial state */
|
|
tt->reads.buf = tt->reads.buf_init;
|
|
tt->reads.buf_len = tt->reads.buf_init_len;
|
|
|
|
len = maxsize ? maxsize : (tt->reads.buf_len);
|
|
if (len > (tt->reads.buf_len)) /* clip to buffer len */
|
|
len = tt->reads.buf_len;
|
|
|
|
/* the overlapped read will signal this event on I/O completion */
|
|
if (!ResetEvent (tt->reads.overlapped.hEvent))
|
|
dbg_tap("ResetEvent failed\n");
|
|
|
|
status = ReadFile(
|
|
tt->hand,
|
|
buffer,
|
|
len,
|
|
&tt->reads.size,
|
|
&tt->reads.overlapped
|
|
);
|
|
|
|
if (status) /* operation completed immediately? */
|
|
{
|
|
/* since we got an immediate return, we must signal the event object ourselves */
|
|
/* ASSERT (SetEvent (tt->reads.overlapped.hEvent)); */
|
|
if (!SetEvent (tt->reads.overlapped.hEvent))
|
|
dbg_tap("SetEvent failed\n");
|
|
|
|
tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
|
|
tt->reads.status = 0;
|
|
|
|
dbg_tap_win32 ("WIN32 I/O: TAP Read immediate return [%d,%d]\n",
|
|
(int) len,
|
|
(int) tt->reads.size);
|
|
}
|
|
else
|
|
{
|
|
err = GetLastError ();
|
|
if (err == ERROR_IO_PENDING) /* operation queued? */
|
|
{
|
|
tt->reads.iostate = IOSTATE_QUEUED;
|
|
tt->reads.status = err;
|
|
dbg_tap_win32 ("WIN32 I/O: TAP Read queued [%d]\n", (int) len);
|
|
}
|
|
else /* error occurred */
|
|
{
|
|
if (!SetEvent (tt->reads.overlapped.hEvent))
|
|
dbg_tap("SetEvent failed\n");
|
|
|
|
tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
|
|
tt->reads.status = err;
|
|
dbg_tap ("WIN32 I/O: TAP Read error [%d] : %d\n", (int) len, (int) err);
|
|
}
|
|
}
|
|
}
|
|
|
|
return tt->reads.iostate;
|
|
}
|
|
|
|
/* Finalize any pending overlapped IO's */
|
|
int tun_finalize(HANDLE h, struct overlapped_io *io, uint8_t **buf, uint32_t *buf_len)
|
|
{
|
|
int ret = -1;
|
|
BOOL status;
|
|
|
|
switch (io->iostate)
|
|
{
|
|
case IOSTATE_QUEUED:
|
|
status = GetOverlappedResult(
|
|
h,
|
|
&io->overlapped,
|
|
&io->size,
|
|
0u
|
|
);
|
|
if (status)
|
|
{
|
|
/* successful return for a queued operation */
|
|
if (buf)
|
|
{
|
|
*buf = io->buf;
|
|
*buf_len = io->buf_len;
|
|
}
|
|
|
|
ret = io->size;
|
|
io->iostate = IOSTATE_INITIAL;
|
|
|
|
if (!ResetEvent (io->overlapped.hEvent))
|
|
dbg_tap("ResetEvent in finalize failed!\n");
|
|
|
|
dbg_tap_win32 ("WIN32 I/O: TAP Completion success: QUEUED! [%d]\n", ret);
|
|
}
|
|
else
|
|
{
|
|
/* error during a queued operation */
|
|
/* error, or just not completed? */
|
|
ret = 0;
|
|
if (GetLastError() != ERROR_IO_INCOMPLETE)
|
|
{
|
|
/* if no error (i.e. just not finished yet),
|
|
then DON'T execute this code */
|
|
io->iostate = IOSTATE_INITIAL;
|
|
if (!ResetEvent (io->overlapped.hEvent))
|
|
dbg_tap("ResetEvent in finalize failed!\n");
|
|
|
|
dbg_tap("WIN32 I/O: TAP Completion error\n");
|
|
ret = -1; /* There actually was an error */
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IOSTATE_IMMEDIATE_RETURN:
|
|
io->iostate = IOSTATE_INITIAL;
|
|
if (!ResetEvent (io->overlapped.hEvent))
|
|
dbg_tap("ResetEvent in finalize failed!\n");
|
|
|
|
if (io->status)
|
|
{
|
|
/* error return for a non-queued operation */
|
|
SetLastError (io->status);
|
|
ret = -1;
|
|
dbg_tap("WIN32 I/O: TAP Completion non-queued error\n");
|
|
}
|
|
else
|
|
{
|
|
/* successful return for a non-queued operation */
|
|
if (buf)
|
|
*buf = io->buf;
|
|
|
|
ret = io->size;
|
|
dbg_tap_win32 ("WIN32 I/O: TAP Completion non-queued success [%d]\n", ret);
|
|
}
|
|
|
|
break;
|
|
|
|
case IOSTATE_INITIAL: /* were we called without proper queueing? */
|
|
SetLastError (ERROR_INVALID_FUNCTION);
|
|
ret = -1;
|
|
dbg_tap ("WIN32 I/O: TAP Completion BAD STATE\n");
|
|
break;
|
|
|
|
default:
|
|
dbg_tap ("Some weird case happened..\n");
|
|
}
|
|
|
|
if (buf)
|
|
*buf_len = ret;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/* returns the amount of bytes written */
|
|
int tun_write_queue (struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
if (tt->writes.iostate == IOSTATE_INITIAL)
|
|
{
|
|
BOOL status;
|
|
int err;
|
|
|
|
/* make a private copy of buf */
|
|
tt->writes.buf = tt->writes.buf_init;
|
|
tt->writes.buf_len = buf_len;
|
|
memcpy(tt->writes.buf, buf, buf_len);
|
|
|
|
/* the overlapped write will signal this event on I/O completion */
|
|
if (!ResetEvent (tt->writes.overlapped.hEvent))
|
|
dbg_tap("ResetEvent in write_queue failed!\n");
|
|
|
|
status = WriteFile(
|
|
tt->hand,
|
|
tt->writes.buf,
|
|
tt->writes.buf_len,
|
|
&tt->writes.size,
|
|
&tt->writes.overlapped
|
|
);
|
|
|
|
if (status) /* operation completed immediately? */
|
|
{
|
|
tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
|
|
|
|
/* since we got an immediate return, we must signal the event object ourselves */
|
|
if (!SetEvent (tt->writes.overlapped.hEvent))
|
|
dbg_tap("SetEvent in write_queue failed!\n");
|
|
|
|
tt->writes.status = 0;
|
|
|
|
dbg_tap_win32 ("WIN32 I/O: TAP Write immediate return [%d,%d]\n",
|
|
(int)(tt->writes.buf_len),
|
|
(int)tt->writes.size);
|
|
}
|
|
else
|
|
{
|
|
err = GetLastError ();
|
|
if (err == ERROR_IO_PENDING) /* operation queued? */
|
|
{
|
|
tt->writes.iostate = IOSTATE_QUEUED;
|
|
tt->writes.status = err;
|
|
dbg_tap_win32("WIN32 I/O: TAP Write queued [%d]\n",
|
|
(tt->writes.buf_len));
|
|
}
|
|
else /* error occurred */
|
|
{
|
|
if (!SetEvent (tt->writes.overlapped.hEvent))
|
|
dbg_tap("SetEvent in write_queue failed!\n");
|
|
|
|
tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
|
|
tt->writes.status = err;
|
|
dbg_tap ("WIN32 I/O: TAP Write error [%d] : %d\n", (int) &tt->writes.buf_len, (int) err);
|
|
}
|
|
}
|
|
}
|
|
|
|
return tt->writes.iostate;
|
|
}
|
|
|
|
static inline int overlapped_io_active (struct overlapped_io *o)
|
|
{
|
|
return o->iostate == IOSTATE_QUEUED || o->iostate == IOSTATE_IMMEDIATE_RETURN;
|
|
}
|
|
|
|
/* if >= 0: returns the amount of bytes read, otherwise error! */
|
|
static int tun_write_win32 (struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
int err = 0;
|
|
int status = 0;
|
|
if (overlapped_io_active (&tt->writes))
|
|
{
|
|
status = tun_finalize (tt->hand, &tt->writes, NULL, 0);
|
|
if (status == 0)
|
|
{
|
|
/* busy, just wait, do not schedule a new write */
|
|
return 0;
|
|
}
|
|
|
|
if (status < 0)
|
|
err = GetLastError ();
|
|
}
|
|
|
|
/* the overlapped IO is done, now we can schedule a new write */
|
|
tun_write_queue (tt, buf, buf_len);
|
|
if (status < 0)
|
|
{
|
|
SetLastError (err);
|
|
return status;
|
|
}
|
|
else
|
|
return buf_len;
|
|
}
|
|
|
|
|
|
/* if >= 0: returns the amount of bytes read, otherwise error! */
|
|
static int tun_read_win32 (struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
int err = 0;
|
|
int status = 0;
|
|
|
|
|
|
/* First, finish possible pending IOs */
|
|
if (overlapped_io_active (&tt->reads))
|
|
{
|
|
status = tun_finalize (tt->hand, &tt->reads, &buf, &buf_len);
|
|
if (status == 0)
|
|
{
|
|
/* busy, just wait, do not schedule a new read */
|
|
return 0;
|
|
}
|
|
|
|
if (status < 0)
|
|
{
|
|
dbg_tap ("tun_finalize status < 0: %d\n", status);
|
|
err = GetLastError ();
|
|
}
|
|
|
|
if (status > 0)
|
|
{
|
|
return buf_len;
|
|
}
|
|
}
|
|
|
|
/* If no pending IOs, schedule a new read */
|
|
/* queue, or immediate return */
|
|
if (IOSTATE_IMMEDIATE_RETURN == tun_read_queue(tt, buf, buf_len))
|
|
{
|
|
return tt->reads.size;
|
|
}
|
|
|
|
/* If the pending IOs gave an error, report it */
|
|
if (status < 0)
|
|
{
|
|
SetLastError (err);
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
/* no errors, but the newly scheduled read is now pending */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int read_tun_buffered(struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
return tun_read_win32 (tt, buf, buf_len);
|
|
}
|
|
|
|
static int write_tun_buffered(struct tuntap *tt, uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
return tun_write_win32 (tt, buf, buf_len);
|
|
}
|
|
|
|
|
|
static int pico_tap_send(struct pico_device *dev, void *buf, int len)
|
|
{
|
|
uint32_t bytes_sent = 0;
|
|
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
|
|
|
|
/* Increase the statistic count */
|
|
tap->statistics_frames_out++;
|
|
|
|
bytes_sent = write_tun_buffered (tap->tt, buf, len);
|
|
dbg_tap("TX> sent %d bytes\n", bytes_sent);
|
|
|
|
/* Discard the frame content silently. */
|
|
return bytes_sent;
|
|
}
|
|
|
|
uint8_t recv_buffer[1500];
|
|
|
|
static int pico_tap_poll(struct pico_device *dev, int loop_score)
|
|
{
|
|
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
|
|
while (loop_score)
|
|
{
|
|
int bytes_read = read_tun_buffered(tap->tt, recv_buffer, 1500);
|
|
loop_score--;
|
|
if (bytes_read > 0)
|
|
{
|
|
dbg_tap("RX< recvd: %d bytes\n", bytes_read);
|
|
pico_stack_recv(dev, recv_buffer, bytes_read);
|
|
/* break; */
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return loop_score;
|
|
}
|
|
|
|
|
|
|
|
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
|
|
|
void overlapped_io_init (struct overlapped_io *o, int event_state)
|
|
{
|
|
CLEAR (*o);
|
|
|
|
/* manual reset event, initially set according to event_state */
|
|
o->overlapped.hEvent = CreateEvent (NULL, TRUE, event_state, NULL);
|
|
if (o->overlapped.hEvent == NULL)
|
|
dbg_tap ("Error: overlapped_io_init: CreateEvent failed\n");
|
|
|
|
/* allocate buffer for overlapped I/O */
|
|
o->buf_init = PICO_ZALLOC(1500); /* XXX: MTU */
|
|
o->buf_init_len = 1500; /* XXX: MTU */
|
|
if (!(o->buf_init))
|
|
dbg_tap("buffer alloc failed!\n"); /* XXX: return -1 or so? */
|
|
else
|
|
dbg_tap("overlapped_io_init buffer allocated!\n");
|
|
}
|
|
|
|
void init_tun_post (struct tuntap *tt)
|
|
{
|
|
dbg_tap("TUN post init (for overlapped io)\n");
|
|
overlapped_io_init (&tt->reads, FALSE);
|
|
overlapped_io_init (&tt->writes, TRUE);
|
|
tt->rw_handle.read = tt->reads.overlapped.hEvent;
|
|
tt->rw_handle.write = tt->writes.overlapped.hEvent;
|
|
}
|
|
|
|
|
|
/*
|
|
* Public interface: pico_tap_create
|
|
* TODO: pico_tap_destroy
|
|
*/
|
|
|
|
struct pico_device *pico_tap_create(char *name, uint8_t *mac)
|
|
{
|
|
struct pico_device_tap *tap = PICO_ZALLOC(sizeof(struct pico_device_tap));
|
|
struct tuntap *tt = PICO_ZALLOC(sizeof(struct tuntap), 1);
|
|
|
|
if (!(tap) || !(tt))
|
|
return NULL;
|
|
|
|
tap->dev.overhead = 0;
|
|
tap->statistics_frames_out = 0;
|
|
tap->dev.send = pico_tap_send;
|
|
tap->dev.poll = pico_tap_poll;
|
|
|
|
show_tap_win_adapters();
|
|
|
|
tt->type = DEV_TYPE_TAP;
|
|
if (open_tun(NULL, NULL, "tap0", tt))
|
|
{
|
|
dbg_tap("Failed to create TAP device!\n");
|
|
PICO_FREE(tt);
|
|
PICO_FREE(tap);
|
|
return NULL;
|
|
}
|
|
|
|
tap->tt = tt;
|
|
|
|
if( 0 != pico_device_init((struct pico_device *)tap, name, mac)) {
|
|
return NULL;
|
|
}
|
|
|
|
init_tun_post(tt); /* init overlapped io */
|
|
|
|
dbg_tap("Device %s created.\n", tap->dev.name);
|
|
|
|
return (struct pico_device *)tap;
|
|
}
|