From patchwork Thu Oct 22 16:49:04 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark McLoughlin X-Patchwork-Id: 36716 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id EE6A8B7BB8 for ; Fri, 23 Oct 2009 04:30:57 +1100 (EST) Received: from localhost ([127.0.0.1]:47619 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N11Uv-0004Hn-9V for incoming@patchwork.ozlabs.org; Thu, 22 Oct 2009 13:30:49 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N10sh-0006Fq-PB for qemu-devel@nongnu.org; Thu, 22 Oct 2009 12:51:20 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N10sb-00067e-6V for qemu-devel@nongnu.org; Thu, 22 Oct 2009 12:51:17 -0400 Received: from [199.232.76.173] (port=54106 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N10sa-00066a-He for qemu-devel@nongnu.org; Thu, 22 Oct 2009 12:51:12 -0400 Received: from mx1.redhat.com ([209.132.183.28]:17365) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1N10sY-00042E-Ty for qemu-devel@nongnu.org; Thu, 22 Oct 2009 12:51:11 -0400 Received: from int-mx08.intmail.prod.int.phx2.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id n9MGp9HJ004295 for ; Thu, 22 Oct 2009 12:51:10 -0400 Received: from blaa.localdomain (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx08.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id n9MGp85N021033; Thu, 22 Oct 2009 12:51:09 -0400 Received: by blaa.localdomain (Postfix, from userid 500) id 0C48F45806; Thu, 22 Oct 2009 17:49:17 +0100 (IST) From: Mark McLoughlin To: qemu-devel@nongnu.org Date: Thu, 22 Oct 2009 17:49:04 +0100 Message-Id: <1256230156-29652-4-git-send-email-markmc@redhat.com> In-Reply-To: <1256230156-29652-1-git-send-email-markmc@redhat.com> References: <1256230156-29652-1-git-send-email-markmc@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.21 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. Cc: Mark McLoughlin Subject: [Qemu-devel] [PATCH 03/15] net: move tap-win32.c under net/ X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Mark McLoughlin --- Makefile | 2 +- net/tap-win32.c | 690 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tap-win32.c | 690 ------------------------------------------------------- 3 files changed, 691 insertions(+), 691 deletions(-) create mode 100644 net/tap-win32.c delete mode 100644 tap-win32.c diff --git a/Makefile b/Makefile index c8c0b18..a49177f 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,7 @@ block-obj-y += $(addprefix block/, $(block-nested-y)) net-obj-y = net.o net-nested-y = queue.o checksum.o +net-nested-$(CONFIG_WIN32) += tap-win32.o net-obj-y += $(addprefix net/, $(net-nested-y)) ###################################################################### @@ -132,7 +133,6 @@ obj-y += qdev.o qdev-properties.o obj-y += qint.o qstring.o qdict.o qlist.o qemu-config.o obj-$(CONFIG_BRLAPI) += baum.o -obj-$(CONFIG_WIN32) += tap-win32.o obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) diff --git a/net/tap-win32.c b/net/tap-win32.c new file mode 100644 index 0000000..7d92df2 --- /dev/null +++ b/net/tap-win32.c @@ -0,0 +1,690 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) James Yonan, 2003-2004, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, see . + */ +#include "qemu-common.h" +#include "net.h" +#include "sysemu.h" +#include +#include +#include + +//============= +// TAP IOCTLs +//============= + +#define TAP_CONTROL_CODE(request,method) \ + CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) + +#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) +#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) +#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) +#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) +#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) +#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) + +//================= +// Registry keys +//================= + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +//====================== +// Filesystem prefixes +//====================== + +#define USERMODEDEVICEDIR "\\\\.\\Global\\" +#define TAPSUFFIX ".tap" + + +//====================== +// Compile time configuration +//====================== + +//#define DEBUG_TAP_WIN32 + +#define TUN_ASYNCHRONOUS_WRITES 1 + +#define TUN_BUFFER_SIZE 1560 +#define TUN_MAX_BUFFER_COUNT 32 + +/* + * The data member "buffer" must be the first element in the tun_buffer + * structure. See the function, tap_win32_free_buffer. + */ +typedef struct tun_buffer_s { + unsigned char buffer [TUN_BUFFER_SIZE]; + unsigned long read_size; + struct tun_buffer_s* next; +} tun_buffer_t; + +typedef struct tap_win32_overlapped { + HANDLE handle; + HANDLE read_event; + HANDLE write_event; + HANDLE output_queue_semaphore; + HANDLE free_list_semaphore; + HANDLE tap_semaphore; + CRITICAL_SECTION output_queue_cs; + CRITICAL_SECTION free_list_cs; + OVERLAPPED read_overlapped; + OVERLAPPED write_overlapped; + tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT]; + tun_buffer_t* free_list; + tun_buffer_t* output_queue_front; + tun_buffer_t* output_queue_back; +} tap_win32_overlapped_t; + +static tap_win32_overlapped_t tap_overlapped; + +static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped) +{ + tun_buffer_t* buffer = NULL; + WaitForSingleObject(overlapped->free_list_semaphore, INFINITE); + EnterCriticalSection(&overlapped->free_list_cs); + buffer = overlapped->free_list; +// assert(buffer != NULL); + overlapped->free_list = buffer->next; + LeaveCriticalSection(&overlapped->free_list_cs); + buffer->next = NULL; + return buffer; +} + +static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) +{ + EnterCriticalSection(&overlapped->free_list_cs); + buffer->next = overlapped->free_list; + overlapped->free_list = buffer; + LeaveCriticalSection(&overlapped->free_list_cs); + ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL); +} + +static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block) +{ + tun_buffer_t* buffer = NULL; + DWORD result, timeout = block ? INFINITE : 0L; + + // Non-blocking call + result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout); + + switch (result) + { + // The semaphore object was signaled. + case WAIT_OBJECT_0: + EnterCriticalSection(&overlapped->output_queue_cs); + + buffer = overlapped->output_queue_front; + overlapped->output_queue_front = buffer->next; + + if(overlapped->output_queue_front == NULL) { + overlapped->output_queue_back = NULL; + } + + LeaveCriticalSection(&overlapped->output_queue_cs); + break; + + // Semaphore was nonsignaled, so a time-out occurred. + case WAIT_TIMEOUT: + // Cannot open another window. + break; + } + + return buffer; +} + +static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped) +{ + return get_buffer_from_output_queue(overlapped, 0); +} + +static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) +{ + EnterCriticalSection(&overlapped->output_queue_cs); + + if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) { + overlapped->output_queue_front = overlapped->output_queue_back = buffer; + } else { + buffer->next = NULL; + overlapped->output_queue_back->next = buffer; + overlapped->output_queue_back = buffer; + } + + LeaveCriticalSection(&overlapped->output_queue_cs); + + ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL); +} + + +static int is_tap_win32_dev(const char *guid) +{ + HKEY netcard_key; + LONG status; + DWORD len; + int i = 0; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + ADAPTER_KEY, + 0, + KEY_READ, + &netcard_key); + + if (status != ERROR_SUCCESS) { + return FALSE; + } + + for (;;) { + 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( + netcard_key, + i, + enum_name, + &len, + NULL, + NULL, + NULL, + NULL); + + if (status == ERROR_NO_MORE_ITEMS) + break; + else if (status != ERROR_SUCCESS) { + return FALSE; + } + + 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) { + return FALSE; + } 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)) { + 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_COMPONENT_ID) &&*/ + !strcmp (net_cfg_instance_id, guid)) { + RegCloseKey (unit_key); + RegCloseKey (netcard_key); + return TRUE; + } + } + } + RegCloseKey (unit_key); + } + ++i; + } + + RegCloseKey (netcard_key); + return FALSE; +} + +static int get_device_guid( + char *name, + int name_size, + char *actual_name, + int actual_name_size) +{ + LONG status; + HKEY control_net_key; + DWORD len; + int i = 0; + int stop = 0; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + NETWORK_CONNECTIONS_KEY, + 0, + KEY_READ, + &control_net_key); + + if (status != ERROR_SUCCESS) { + return -1; + } + + while (!stop) + { + char enum_name[256]; + char connection_string[256]; + HKEY connection_key; + char name_data[256]; + DWORD name_type; + const char name_string[] = "Name"; + + len = sizeof (enum_name); + status = RegEnumKeyEx( + control_net_key, + i, + enum_name, + &len, + NULL, + NULL, + NULL, + NULL); + + if (status == ERROR_NO_MORE_ITEMS) + break; + else if (status != ERROR_SUCCESS) { + return -1; + } + + 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) { + len = sizeof (name_data); + status = RegQueryValueEx( + connection_key, + name_string, + NULL, + &name_type, + (LPBYTE)name_data, + &len); + + if (status != ERROR_SUCCESS || name_type != REG_SZ) { + return -1; + } + else { + if (is_tap_win32_dev(enum_name)) { + snprintf(name, name_size, "%s", enum_name); + if (actual_name) { + if (strcmp(actual_name, "") != 0) { + if (strcmp(name_data, actual_name) != 0) { + RegCloseKey (connection_key); + ++i; + continue; + } + } + else { + snprintf(actual_name, actual_name_size, "%s", name_data); + } + } + stop = 1; + } + } + + RegCloseKey (connection_key); + } + ++i; + } + + RegCloseKey (control_net_key); + + if (stop == 0) + return -1; + + return 0; +} + +static int tap_win32_set_status(HANDLE handle, int status) +{ + unsigned long len = 0; + + return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS, + &status, sizeof (status), + &status, sizeof (status), &len, NULL); +} + +static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle) +{ + overlapped->handle = handle; + + overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL); + overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL); + + overlapped->read_overlapped.Offset = 0; + overlapped->read_overlapped.OffsetHigh = 0; + overlapped->read_overlapped.hEvent = overlapped->read_event; + + overlapped->write_overlapped.Offset = 0; + overlapped->write_overlapped.OffsetHigh = 0; + overlapped->write_overlapped.hEvent = overlapped->write_event; + + InitializeCriticalSection(&overlapped->output_queue_cs); + InitializeCriticalSection(&overlapped->free_list_cs); + + overlapped->output_queue_semaphore = CreateSemaphore( + NULL, // default security attributes + 0, // initial count + TUN_MAX_BUFFER_COUNT, // maximum count + NULL); // unnamed semaphore + + if(!overlapped->output_queue_semaphore) { + fprintf(stderr, "error creating output queue semaphore!\n"); + } + + overlapped->free_list_semaphore = CreateSemaphore( + NULL, // default security attributes + TUN_MAX_BUFFER_COUNT, // initial count + TUN_MAX_BUFFER_COUNT, // maximum count + NULL); // unnamed semaphore + + if(!overlapped->free_list_semaphore) { + fprintf(stderr, "error creating free list semaphore!\n"); + } + + overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL; + + { + unsigned index; + for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) { + tun_buffer_t* element = &overlapped->buffers[index]; + element->next = overlapped->free_list; + overlapped->free_list = element; + } + } + /* To count buffers, initially no-signal. */ + overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL); + if(!overlapped->tap_semaphore) + fprintf(stderr, "error creating tap_semaphore.\n"); +} + +static int tap_win32_write(tap_win32_overlapped_t *overlapped, + const void *buffer, unsigned long size) +{ + unsigned long write_size; + BOOL result; + DWORD error; + + result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped, + &write_size, FALSE); + + if (!result && GetLastError() == ERROR_IO_INCOMPLETE) + WaitForSingleObject(overlapped->write_event, INFINITE); + + result = WriteFile(overlapped->handle, buffer, size, + &write_size, &overlapped->write_overlapped); + + if (!result) { + switch (error = GetLastError()) + { + case ERROR_IO_PENDING: +#ifndef TUN_ASYNCHRONOUS_WRITES + WaitForSingleObject(overlapped->write_event, INFINITE); +#endif + break; + default: + return -1; + } + } + + return 0; +} + +static DWORD WINAPI tap_win32_thread_entry(LPVOID param) +{ + tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param; + unsigned long read_size; + BOOL result; + DWORD dwError; + tun_buffer_t* buffer = get_buffer_from_free_list(overlapped); + + + for (;;) { + result = ReadFile(overlapped->handle, + buffer->buffer, + sizeof(buffer->buffer), + &read_size, + &overlapped->read_overlapped); + if (!result) { + dwError = GetLastError(); + if (dwError == ERROR_IO_PENDING) { + WaitForSingleObject(overlapped->read_event, INFINITE); + result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped, + &read_size, FALSE); + if (!result) { +#ifdef DEBUG_TAP_WIN32 + LPVOID lpBuffer; + dwError = GetLastError(); + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpBuffer, 0, NULL ); + fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer); + LocalFree( lpBuffer ); +#endif + } + } else { +#ifdef DEBUG_TAP_WIN32 + LPVOID lpBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpBuffer, 0, NULL ); + fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer); + LocalFree( lpBuffer ); +#endif + } + } + + if(read_size > 0) { + buffer->read_size = read_size; + put_buffer_on_output_queue(overlapped, buffer); + ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL); + buffer = get_buffer_from_free_list(overlapped); + } + } + + return 0; +} + +static int tap_win32_read(tap_win32_overlapped_t *overlapped, + uint8_t **pbuf, int max_size) +{ + int size = 0; + + tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped); + + if(buffer != NULL) { + *pbuf = buffer->buffer; + size = (int)buffer->read_size; + if(size > max_size) { + size = max_size; + } + } + + return size; +} + +static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, + uint8_t *pbuf) +{ + tun_buffer_t* buffer = (tun_buffer_t*)pbuf; + put_buffer_on_free_list(overlapped, buffer); +} + +static int tap_win32_open(tap_win32_overlapped_t **phandle, + const char *prefered_name) +{ + char device_path[256]; + char device_guid[0x100]; + int rc; + HANDLE handle; + BOOL bret; + char name_buffer[0x100] = {0, }; + struct { + unsigned long major; + unsigned long minor; + unsigned long debug; + } version; + DWORD version_len; + DWORD idThread; + HANDLE hThread; + + if (prefered_name != NULL) + snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name); + + rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)); + if (rc) + return -1; + + snprintf (device_path, sizeof(device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAPSUFFIX); + + handle = CreateFile ( + device_path, + GENERIC_READ | GENERIC_WRITE, + 0, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 ); + + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION, + &version, sizeof (version), + &version, sizeof (version), &version_len, NULL); + + if (bret == FALSE) { + CloseHandle(handle); + return -1; + } + + if (!tap_win32_set_status(handle, TRUE)) { + return -1; + } + + tap_win32_overlapped_init(&tap_overlapped, handle); + + *phandle = &tap_overlapped; + + hThread = CreateThread(NULL, 0, tap_win32_thread_entry, + (LPVOID)&tap_overlapped, 0, &idThread); + return 0; +} + +/********************************************/ + + typedef struct TAPState { + VLANClientState *vc; + tap_win32_overlapped_t *handle; + } TAPState; + +static void tap_cleanup(VLANClientState *vc) +{ + TAPState *s = vc->opaque; + + qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL); + + /* FIXME: need to kill thread and close file handle: + tap_win32_close(s); + */ + qemu_free(s); +} + +static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +{ + TAPState *s = vc->opaque; + + return tap_win32_write(s->handle, buf, size); +} + +static void tap_win32_send(void *opaque) +{ + TAPState *s = opaque; + uint8_t *buf; + int max_size = 4096; + int size; + + size = tap_win32_read(s->handle, &buf, max_size); + if (size > 0) { + qemu_send_packet(s->vc, buf, size); + tap_win32_free_buffer(s->handle, buf); + } +} + +int tap_win32_init(VLANState *vlan, const char *model, + const char *name, const char *ifname) +{ + TAPState *s; + + s = qemu_mallocz(sizeof(TAPState)); + if (!s) + return -1; + if (tap_win32_open(&s->handle, ifname) < 0) { + printf("tap: Could not open '%s'\n", ifname); + return -1; + } + + s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_TAP, + vlan, NULL, model, name, + NULL, tap_receive, + NULL, NULL, tap_cleanup, s); + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "tap: ifname=%s", ifname); + + qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s); + return 0; +} diff --git a/tap-win32.c b/tap-win32.c deleted file mode 100644 index 7d92df2..0000000 --- a/tap-win32.c +++ /dev/null @@ -1,690 +0,0 @@ -/* - * TAP-Win32 -- A kernel driver to provide virtual tap device functionality - * on Windows. Originally derived from the CIPE-Win32 - * project by Damion K. Wilson, with extensive modifications by - * James Yonan. - * - * All source code which derives from the CIPE-Win32 project is - * Copyright (C) Damion K. Wilson, 2003, and is released under the - * GPL version 2 (see below). - * - * All other source code is Copyright (C) James Yonan, 2003-2004, - * and is released under the GPL version 2 (see below). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (see the file COPYING included with this - * distribution); if not, see . - */ -#include "qemu-common.h" -#include "net.h" -#include "sysemu.h" -#include -#include -#include - -//============= -// TAP IOCTLs -//============= - -#define TAP_CONTROL_CODE(request,method) \ - CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) - -#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) -#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) -#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) -#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) -#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) -#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) - -//================= -// Registry keys -//================= - -#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" - -#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" - -//====================== -// Filesystem prefixes -//====================== - -#define USERMODEDEVICEDIR "\\\\.\\Global\\" -#define TAPSUFFIX ".tap" - - -//====================== -// Compile time configuration -//====================== - -//#define DEBUG_TAP_WIN32 - -#define TUN_ASYNCHRONOUS_WRITES 1 - -#define TUN_BUFFER_SIZE 1560 -#define TUN_MAX_BUFFER_COUNT 32 - -/* - * The data member "buffer" must be the first element in the tun_buffer - * structure. See the function, tap_win32_free_buffer. - */ -typedef struct tun_buffer_s { - unsigned char buffer [TUN_BUFFER_SIZE]; - unsigned long read_size; - struct tun_buffer_s* next; -} tun_buffer_t; - -typedef struct tap_win32_overlapped { - HANDLE handle; - HANDLE read_event; - HANDLE write_event; - HANDLE output_queue_semaphore; - HANDLE free_list_semaphore; - HANDLE tap_semaphore; - CRITICAL_SECTION output_queue_cs; - CRITICAL_SECTION free_list_cs; - OVERLAPPED read_overlapped; - OVERLAPPED write_overlapped; - tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT]; - tun_buffer_t* free_list; - tun_buffer_t* output_queue_front; - tun_buffer_t* output_queue_back; -} tap_win32_overlapped_t; - -static tap_win32_overlapped_t tap_overlapped; - -static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped) -{ - tun_buffer_t* buffer = NULL; - WaitForSingleObject(overlapped->free_list_semaphore, INFINITE); - EnterCriticalSection(&overlapped->free_list_cs); - buffer = overlapped->free_list; -// assert(buffer != NULL); - overlapped->free_list = buffer->next; - LeaveCriticalSection(&overlapped->free_list_cs); - buffer->next = NULL; - return buffer; -} - -static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) -{ - EnterCriticalSection(&overlapped->free_list_cs); - buffer->next = overlapped->free_list; - overlapped->free_list = buffer; - LeaveCriticalSection(&overlapped->free_list_cs); - ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL); -} - -static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block) -{ - tun_buffer_t* buffer = NULL; - DWORD result, timeout = block ? INFINITE : 0L; - - // Non-blocking call - result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout); - - switch (result) - { - // The semaphore object was signaled. - case WAIT_OBJECT_0: - EnterCriticalSection(&overlapped->output_queue_cs); - - buffer = overlapped->output_queue_front; - overlapped->output_queue_front = buffer->next; - - if(overlapped->output_queue_front == NULL) { - overlapped->output_queue_back = NULL; - } - - LeaveCriticalSection(&overlapped->output_queue_cs); - break; - - // Semaphore was nonsignaled, so a time-out occurred. - case WAIT_TIMEOUT: - // Cannot open another window. - break; - } - - return buffer; -} - -static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped) -{ - return get_buffer_from_output_queue(overlapped, 0); -} - -static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) -{ - EnterCriticalSection(&overlapped->output_queue_cs); - - if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) { - overlapped->output_queue_front = overlapped->output_queue_back = buffer; - } else { - buffer->next = NULL; - overlapped->output_queue_back->next = buffer; - overlapped->output_queue_back = buffer; - } - - LeaveCriticalSection(&overlapped->output_queue_cs); - - ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL); -} - - -static int is_tap_win32_dev(const char *guid) -{ - HKEY netcard_key; - LONG status; - DWORD len; - int i = 0; - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - ADAPTER_KEY, - 0, - KEY_READ, - &netcard_key); - - if (status != ERROR_SUCCESS) { - return FALSE; - } - - for (;;) { - 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( - netcard_key, - i, - enum_name, - &len, - NULL, - NULL, - NULL, - NULL); - - if (status == ERROR_NO_MORE_ITEMS) - break; - else if (status != ERROR_SUCCESS) { - return FALSE; - } - - 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) { - return FALSE; - } 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)) { - 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_COMPONENT_ID) &&*/ - !strcmp (net_cfg_instance_id, guid)) { - RegCloseKey (unit_key); - RegCloseKey (netcard_key); - return TRUE; - } - } - } - RegCloseKey (unit_key); - } - ++i; - } - - RegCloseKey (netcard_key); - return FALSE; -} - -static int get_device_guid( - char *name, - int name_size, - char *actual_name, - int actual_name_size) -{ - LONG status; - HKEY control_net_key; - DWORD len; - int i = 0; - int stop = 0; - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - NETWORK_CONNECTIONS_KEY, - 0, - KEY_READ, - &control_net_key); - - if (status != ERROR_SUCCESS) { - return -1; - } - - while (!stop) - { - char enum_name[256]; - char connection_string[256]; - HKEY connection_key; - char name_data[256]; - DWORD name_type; - const char name_string[] = "Name"; - - len = sizeof (enum_name); - status = RegEnumKeyEx( - control_net_key, - i, - enum_name, - &len, - NULL, - NULL, - NULL, - NULL); - - if (status == ERROR_NO_MORE_ITEMS) - break; - else if (status != ERROR_SUCCESS) { - return -1; - } - - 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) { - len = sizeof (name_data); - status = RegQueryValueEx( - connection_key, - name_string, - NULL, - &name_type, - (LPBYTE)name_data, - &len); - - if (status != ERROR_SUCCESS || name_type != REG_SZ) { - return -1; - } - else { - if (is_tap_win32_dev(enum_name)) { - snprintf(name, name_size, "%s", enum_name); - if (actual_name) { - if (strcmp(actual_name, "") != 0) { - if (strcmp(name_data, actual_name) != 0) { - RegCloseKey (connection_key); - ++i; - continue; - } - } - else { - snprintf(actual_name, actual_name_size, "%s", name_data); - } - } - stop = 1; - } - } - - RegCloseKey (connection_key); - } - ++i; - } - - RegCloseKey (control_net_key); - - if (stop == 0) - return -1; - - return 0; -} - -static int tap_win32_set_status(HANDLE handle, int status) -{ - unsigned long len = 0; - - return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS, - &status, sizeof (status), - &status, sizeof (status), &len, NULL); -} - -static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle) -{ - overlapped->handle = handle; - - overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL); - overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL); - - overlapped->read_overlapped.Offset = 0; - overlapped->read_overlapped.OffsetHigh = 0; - overlapped->read_overlapped.hEvent = overlapped->read_event; - - overlapped->write_overlapped.Offset = 0; - overlapped->write_overlapped.OffsetHigh = 0; - overlapped->write_overlapped.hEvent = overlapped->write_event; - - InitializeCriticalSection(&overlapped->output_queue_cs); - InitializeCriticalSection(&overlapped->free_list_cs); - - overlapped->output_queue_semaphore = CreateSemaphore( - NULL, // default security attributes - 0, // initial count - TUN_MAX_BUFFER_COUNT, // maximum count - NULL); // unnamed semaphore - - if(!overlapped->output_queue_semaphore) { - fprintf(stderr, "error creating output queue semaphore!\n"); - } - - overlapped->free_list_semaphore = CreateSemaphore( - NULL, // default security attributes - TUN_MAX_BUFFER_COUNT, // initial count - TUN_MAX_BUFFER_COUNT, // maximum count - NULL); // unnamed semaphore - - if(!overlapped->free_list_semaphore) { - fprintf(stderr, "error creating free list semaphore!\n"); - } - - overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL; - - { - unsigned index; - for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) { - tun_buffer_t* element = &overlapped->buffers[index]; - element->next = overlapped->free_list; - overlapped->free_list = element; - } - } - /* To count buffers, initially no-signal. */ - overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL); - if(!overlapped->tap_semaphore) - fprintf(stderr, "error creating tap_semaphore.\n"); -} - -static int tap_win32_write(tap_win32_overlapped_t *overlapped, - const void *buffer, unsigned long size) -{ - unsigned long write_size; - BOOL result; - DWORD error; - - result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped, - &write_size, FALSE); - - if (!result && GetLastError() == ERROR_IO_INCOMPLETE) - WaitForSingleObject(overlapped->write_event, INFINITE); - - result = WriteFile(overlapped->handle, buffer, size, - &write_size, &overlapped->write_overlapped); - - if (!result) { - switch (error = GetLastError()) - { - case ERROR_IO_PENDING: -#ifndef TUN_ASYNCHRONOUS_WRITES - WaitForSingleObject(overlapped->write_event, INFINITE); -#endif - break; - default: - return -1; - } - } - - return 0; -} - -static DWORD WINAPI tap_win32_thread_entry(LPVOID param) -{ - tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param; - unsigned long read_size; - BOOL result; - DWORD dwError; - tun_buffer_t* buffer = get_buffer_from_free_list(overlapped); - - - for (;;) { - result = ReadFile(overlapped->handle, - buffer->buffer, - sizeof(buffer->buffer), - &read_size, - &overlapped->read_overlapped); - if (!result) { - dwError = GetLastError(); - if (dwError == ERROR_IO_PENDING) { - WaitForSingleObject(overlapped->read_event, INFINITE); - result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped, - &read_size, FALSE); - if (!result) { -#ifdef DEBUG_TAP_WIN32 - LPVOID lpBuffer; - dwError = GetLastError(); - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) & lpBuffer, 0, NULL ); - fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer); - LocalFree( lpBuffer ); -#endif - } - } else { -#ifdef DEBUG_TAP_WIN32 - LPVOID lpBuffer; - FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) & lpBuffer, 0, NULL ); - fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer); - LocalFree( lpBuffer ); -#endif - } - } - - if(read_size > 0) { - buffer->read_size = read_size; - put_buffer_on_output_queue(overlapped, buffer); - ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL); - buffer = get_buffer_from_free_list(overlapped); - } - } - - return 0; -} - -static int tap_win32_read(tap_win32_overlapped_t *overlapped, - uint8_t **pbuf, int max_size) -{ - int size = 0; - - tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped); - - if(buffer != NULL) { - *pbuf = buffer->buffer; - size = (int)buffer->read_size; - if(size > max_size) { - size = max_size; - } - } - - return size; -} - -static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, - uint8_t *pbuf) -{ - tun_buffer_t* buffer = (tun_buffer_t*)pbuf; - put_buffer_on_free_list(overlapped, buffer); -} - -static int tap_win32_open(tap_win32_overlapped_t **phandle, - const char *prefered_name) -{ - char device_path[256]; - char device_guid[0x100]; - int rc; - HANDLE handle; - BOOL bret; - char name_buffer[0x100] = {0, }; - struct { - unsigned long major; - unsigned long minor; - unsigned long debug; - } version; - DWORD version_len; - DWORD idThread; - HANDLE hThread; - - if (prefered_name != NULL) - snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name); - - rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)); - if (rc) - return -1; - - snprintf (device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAPSUFFIX); - - handle = CreateFile ( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 ); - - if (handle == INVALID_HANDLE_VALUE) { - return -1; - } - - bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION, - &version, sizeof (version), - &version, sizeof (version), &version_len, NULL); - - if (bret == FALSE) { - CloseHandle(handle); - return -1; - } - - if (!tap_win32_set_status(handle, TRUE)) { - return -1; - } - - tap_win32_overlapped_init(&tap_overlapped, handle); - - *phandle = &tap_overlapped; - - hThread = CreateThread(NULL, 0, tap_win32_thread_entry, - (LPVOID)&tap_overlapped, 0, &idThread); - return 0; -} - -/********************************************/ - - typedef struct TAPState { - VLANClientState *vc; - tap_win32_overlapped_t *handle; - } TAPState; - -static void tap_cleanup(VLANClientState *vc) -{ - TAPState *s = vc->opaque; - - qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL); - - /* FIXME: need to kill thread and close file handle: - tap_win32_close(s); - */ - qemu_free(s); -} - -static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size) -{ - TAPState *s = vc->opaque; - - return tap_win32_write(s->handle, buf, size); -} - -static void tap_win32_send(void *opaque) -{ - TAPState *s = opaque; - uint8_t *buf; - int max_size = 4096; - int size; - - size = tap_win32_read(s->handle, &buf, max_size); - if (size > 0) { - qemu_send_packet(s->vc, buf, size); - tap_win32_free_buffer(s->handle, buf); - } -} - -int tap_win32_init(VLANState *vlan, const char *model, - const char *name, const char *ifname) -{ - TAPState *s; - - s = qemu_mallocz(sizeof(TAPState)); - if (!s) - return -1; - if (tap_win32_open(&s->handle, ifname) < 0) { - printf("tap: Could not open '%s'\n", ifname); - return -1; - } - - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_TAP, - vlan, NULL, model, name, - NULL, tap_receive, - NULL, NULL, tap_cleanup, s); - - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "tap: ifname=%s", ifname); - - qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s); - return 0; -}