/********************************************************************* PicoTCP. Copyright (c) 2014-2015 Altran Intelligent Systems. Some rights reserved. See LICENSE and COPYING 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 #include #include #include "pico_dev_tap_windows_private.h" /* * Debugging info */ #define dbg_tap_info(...) /* tap info messages */ #define dbg_tap(...) /* first level debug */ #define dbg_win32(...) /* second level detailed win32 debug */ #define dbg_reg(...) /* third level: registry debug */ /* * 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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; }