• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  TAP-Win32 -- A kernel driver to provide virtual tap device functionality
3  *               on Windows.  Originally derived from the CIPE-Win32
4  *               project by Damion K. Wilson, with extensive modifications by
5  *               James Yonan.
6  *
7  *  All source code which derives from the CIPE-Win32 project is
8  *  Copyright (C) Damion K. Wilson, 2003, and is released under the
9  *  GPL version 2 (see below).
10  *
11  *  All other source code is Copyright (C) James Yonan, 2003-2004,
12  *  and is released under the GPL version 2 (see below).
13  *
14  *  This program is free software; you can redistribute it and/or modify
15  *  it under the terms of the GNU General Public License as published by
16  *  the Free Software Foundation; either version 2 of the License, or
17  *  (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program (see the file COPYING included with this
26  *  distribution); if not, write to the Free Software Foundation, Inc.,
27  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28  */
29 #include "qemu-common.h"
30 #include "net.h"
31 #include "sysemu.h"
32 #include <stdio.h>
33 #include <windows.h>
34 #include <winioctl.h>
35 
36 //=============
37 // TAP IOCTLs
38 //=============
39 
40 #define TAP_CONTROL_CODE(request,method) \
41   CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
42 
43 #define TAP_IOCTL_GET_MAC               TAP_CONTROL_CODE (1, METHOD_BUFFERED)
44 #define TAP_IOCTL_GET_VERSION           TAP_CONTROL_CODE (2, METHOD_BUFFERED)
45 #define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
46 #define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
47 #define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
48 #define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
49 #define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
50 #define TAP_IOCTL_GET_LOG_LINE          TAP_CONTROL_CODE (8, METHOD_BUFFERED)
51 #define TAP_IOCTL_CONFIG_DHCP_SET_OPT   TAP_CONTROL_CODE (9, METHOD_BUFFERED)
52 
53 //=================
54 // Registry keys
55 //=================
56 
57 #define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
58 
59 #define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
60 
61 //======================
62 // Filesystem prefixes
63 //======================
64 
65 #define USERMODEDEVICEDIR "\\\\.\\Global\\"
66 #define TAPSUFFIX         ".tap"
67 
68 
69 //======================
70 // Compile time configuration
71 //======================
72 
73 //#define DEBUG_TAP_WIN32
74 
75 #define TUN_ASYNCHRONOUS_WRITES 1
76 
77 #define TUN_BUFFER_SIZE 1560
78 #define TUN_MAX_BUFFER_COUNT 32
79 
80 /*
81  * The data member "buffer" must be the first element in the tun_buffer
82  * structure. See the function, tap_win32_free_buffer.
83  */
84 typedef struct tun_buffer_s {
85     unsigned char buffer [TUN_BUFFER_SIZE];
86     unsigned long read_size;
87     struct tun_buffer_s* next;
88 } tun_buffer_t;
89 
90 typedef struct tap_win32_overlapped {
91     HANDLE handle;
92     HANDLE read_event;
93     HANDLE write_event;
94     HANDLE output_queue_semaphore;
95     HANDLE free_list_semaphore;
96     HANDLE tap_semaphore;
97     CRITICAL_SECTION output_queue_cs;
98     CRITICAL_SECTION free_list_cs;
99     OVERLAPPED read_overlapped;
100     OVERLAPPED write_overlapped;
101     tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
102     tun_buffer_t* free_list;
103     tun_buffer_t* output_queue_front;
104     tun_buffer_t* output_queue_back;
105 } tap_win32_overlapped_t;
106 
107 static tap_win32_overlapped_t tap_overlapped;
108 
get_buffer_from_free_list(tap_win32_overlapped_t * const overlapped)109 static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped)
110 {
111     tun_buffer_t* buffer = NULL;
112     WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
113     EnterCriticalSection(&overlapped->free_list_cs);
114     buffer = overlapped->free_list;
115 //    assert(buffer != NULL);
116     overlapped->free_list = buffer->next;
117     LeaveCriticalSection(&overlapped->free_list_cs);
118     buffer->next = NULL;
119     return buffer;
120 }
121 
put_buffer_on_free_list(tap_win32_overlapped_t * const overlapped,tun_buffer_t * const buffer)122 static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
123 {
124     EnterCriticalSection(&overlapped->free_list_cs);
125     buffer->next = overlapped->free_list;
126     overlapped->free_list = buffer;
127     LeaveCriticalSection(&overlapped->free_list_cs);
128     ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
129 }
130 
get_buffer_from_output_queue(tap_win32_overlapped_t * const overlapped,const int block)131 static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block)
132 {
133     tun_buffer_t* buffer = NULL;
134     DWORD result, timeout = block ? INFINITE : 0L;
135 
136     // Non-blocking call
137     result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout);
138 
139     switch (result)
140     {
141         // The semaphore object was signaled.
142         case WAIT_OBJECT_0:
143             EnterCriticalSection(&overlapped->output_queue_cs);
144 
145             buffer = overlapped->output_queue_front;
146             overlapped->output_queue_front = buffer->next;
147 
148             if(overlapped->output_queue_front == NULL) {
149                 overlapped->output_queue_back = NULL;
150             }
151 
152             LeaveCriticalSection(&overlapped->output_queue_cs);
153             break;
154 
155         // Semaphore was nonsignaled, so a time-out occurred.
156         case WAIT_TIMEOUT:
157             // Cannot open another window.
158             break;
159     }
160 
161     return buffer;
162 }
163 
get_buffer_from_output_queue_immediate(tap_win32_overlapped_t * const overlapped)164 static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped)
165 {
166     return get_buffer_from_output_queue(overlapped, 0);
167 }
168 
put_buffer_on_output_queue(tap_win32_overlapped_t * const overlapped,tun_buffer_t * const buffer)169 static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
170 {
171     EnterCriticalSection(&overlapped->output_queue_cs);
172 
173     if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
174         overlapped->output_queue_front = overlapped->output_queue_back = buffer;
175     } else {
176         buffer->next = NULL;
177         overlapped->output_queue_back->next = buffer;
178         overlapped->output_queue_back = buffer;
179     }
180 
181     LeaveCriticalSection(&overlapped->output_queue_cs);
182 
183     ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
184 }
185 
186 
is_tap_win32_dev(const char * guid)187 static int is_tap_win32_dev(const char *guid)
188 {
189     HKEY netcard_key;
190     LONG status;
191     DWORD len;
192     int i = 0;
193 
194     status = RegOpenKeyEx(
195         HKEY_LOCAL_MACHINE,
196         ADAPTER_KEY,
197         0,
198         KEY_READ,
199         &netcard_key);
200 
201     if (status != ERROR_SUCCESS) {
202         return FALSE;
203     }
204 
205     for (;;) {
206         char enum_name[256];
207         char unit_string[256];
208         HKEY unit_key;
209         char component_id_string[] = "ComponentId";
210         char component_id[256];
211         char net_cfg_instance_id_string[] = "NetCfgInstanceId";
212         char net_cfg_instance_id[256];
213         DWORD data_type;
214 
215         len = sizeof (enum_name);
216         status = RegEnumKeyEx(
217             netcard_key,
218             i,
219             enum_name,
220             &len,
221             NULL,
222             NULL,
223             NULL,
224             NULL);
225 
226         if (status == ERROR_NO_MORE_ITEMS)
227             break;
228         else if (status != ERROR_SUCCESS) {
229             return FALSE;
230         }
231 
232         snprintf (unit_string, sizeof(unit_string), "%s\\%s",
233                   ADAPTER_KEY, enum_name);
234 
235         status = RegOpenKeyEx(
236             HKEY_LOCAL_MACHINE,
237             unit_string,
238             0,
239             KEY_READ,
240             &unit_key);
241 
242         if (status != ERROR_SUCCESS) {
243             return FALSE;
244         } else {
245             len = sizeof (component_id);
246             status = RegQueryValueEx(
247                 unit_key,
248                 component_id_string,
249                 NULL,
250                 &data_type,
251                 (LPBYTE)component_id,
252                 &len);
253 
254             if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
255                 len = sizeof (net_cfg_instance_id);
256                 status = RegQueryValueEx(
257                     unit_key,
258                     net_cfg_instance_id_string,
259                     NULL,
260                     &data_type,
261                     (LPBYTE)net_cfg_instance_id,
262                     &len);
263 
264                 if (status == ERROR_SUCCESS && data_type == REG_SZ) {
265                     if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
266                         !strcmp (net_cfg_instance_id, guid)) {
267                         RegCloseKey (unit_key);
268                         RegCloseKey (netcard_key);
269                         return TRUE;
270                     }
271                 }
272             }
273             RegCloseKey (unit_key);
274         }
275         ++i;
276     }
277 
278     RegCloseKey (netcard_key);
279     return FALSE;
280 }
281 
get_device_guid(char * name,int name_size,char * actual_name,int actual_name_size)282 static int get_device_guid(
283     char *name,
284     int name_size,
285     char *actual_name,
286     int actual_name_size)
287 {
288     LONG status;
289     HKEY control_net_key;
290     DWORD len;
291     int i = 0;
292     int stop = 0;
293 
294     status = RegOpenKeyEx(
295         HKEY_LOCAL_MACHINE,
296         NETWORK_CONNECTIONS_KEY,
297         0,
298         KEY_READ,
299         &control_net_key);
300 
301     if (status != ERROR_SUCCESS) {
302         return -1;
303     }
304 
305     while (!stop)
306     {
307         char enum_name[256];
308         char connection_string[256];
309         HKEY connection_key;
310         char name_data[256];
311         DWORD name_type;
312         const char name_string[] = "Name";
313 
314         len = sizeof (enum_name);
315         status = RegEnumKeyEx(
316             control_net_key,
317             i,
318             enum_name,
319             &len,
320             NULL,
321             NULL,
322             NULL,
323             NULL);
324 
325         if (status == ERROR_NO_MORE_ITEMS)
326             break;
327         else if (status != ERROR_SUCCESS) {
328             return -1;
329         }
330 
331         snprintf(connection_string,
332              sizeof(connection_string),
333              "%s\\%s\\Connection",
334              NETWORK_CONNECTIONS_KEY, enum_name);
335 
336         status = RegOpenKeyEx(
337             HKEY_LOCAL_MACHINE,
338             connection_string,
339             0,
340             KEY_READ,
341             &connection_key);
342 
343         if (status == ERROR_SUCCESS) {
344             len = sizeof (name_data);
345             status = RegQueryValueEx(
346                 connection_key,
347                 name_string,
348                 NULL,
349                 &name_type,
350                 (LPBYTE)name_data,
351                 &len);
352 
353             if (status != ERROR_SUCCESS || name_type != REG_SZ) {
354                     return -1;
355             }
356             else {
357                 if (is_tap_win32_dev(enum_name)) {
358                     snprintf(name, name_size, "%s", enum_name);
359                     if (actual_name) {
360                         if (strcmp(actual_name, "") != 0) {
361                             if (strcmp(name_data, actual_name) != 0) {
362                                 RegCloseKey (connection_key);
363                                 ++i;
364                                 continue;
365                             }
366                         }
367                         else {
368                             snprintf(actual_name, actual_name_size, "%s", name_data);
369                         }
370                     }
371                     stop = 1;
372                 }
373             }
374 
375             RegCloseKey (connection_key);
376         }
377         ++i;
378     }
379 
380     RegCloseKey (control_net_key);
381 
382     if (stop == 0)
383         return -1;
384 
385     return 0;
386 }
387 
tap_win32_set_status(HANDLE handle,int status)388 static int tap_win32_set_status(HANDLE handle, int status)
389 {
390     unsigned long len = 0;
391 
392     return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
393                 &status, sizeof (status),
394                 &status, sizeof (status), &len, NULL);
395 }
396 
tap_win32_overlapped_init(tap_win32_overlapped_t * const overlapped,const HANDLE handle)397 static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
398 {
399     overlapped->handle = handle;
400 
401     overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
402     overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
403 
404     overlapped->read_overlapped.Offset = 0;
405     overlapped->read_overlapped.OffsetHigh = 0;
406     overlapped->read_overlapped.hEvent = overlapped->read_event;
407 
408     overlapped->write_overlapped.Offset = 0;
409     overlapped->write_overlapped.OffsetHigh = 0;
410     overlapped->write_overlapped.hEvent = overlapped->write_event;
411 
412     InitializeCriticalSection(&overlapped->output_queue_cs);
413     InitializeCriticalSection(&overlapped->free_list_cs);
414 
415     overlapped->output_queue_semaphore = CreateSemaphore(
416         NULL,   // default security attributes
417         0,   // initial count
418         TUN_MAX_BUFFER_COUNT,   // maximum count
419         NULL);  // unnamed semaphore
420 
421     if(!overlapped->output_queue_semaphore)  {
422         fprintf(stderr, "error creating output queue semaphore!\n");
423     }
424 
425     overlapped->free_list_semaphore = CreateSemaphore(
426         NULL,   // default security attributes
427         TUN_MAX_BUFFER_COUNT,   // initial count
428         TUN_MAX_BUFFER_COUNT,   // maximum count
429         NULL);  // unnamed semaphore
430 
431     if(!overlapped->free_list_semaphore)  {
432         fprintf(stderr, "error creating free list semaphore!\n");
433     }
434 
435     overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
436 
437     {
438         unsigned index;
439         for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
440             tun_buffer_t* element = &overlapped->buffers[index];
441             element->next = overlapped->free_list;
442             overlapped->free_list = element;
443         }
444     }
445     /* To count buffers, initially no-signal. */
446     overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL);
447     if(!overlapped->tap_semaphore)
448         fprintf(stderr, "error creating tap_semaphore.\n");
449 }
450 
tap_win32_write(tap_win32_overlapped_t * overlapped,const void * buffer,unsigned long size)451 static int tap_win32_write(tap_win32_overlapped_t *overlapped,
452                            const void *buffer, unsigned long size)
453 {
454     unsigned long write_size;
455     BOOL result;
456     DWORD error;
457 
458     result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
459                                   &write_size, FALSE);
460 
461     if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
462         WaitForSingleObject(overlapped->write_event, INFINITE);
463 
464     result = WriteFile(overlapped->handle, buffer, size,
465                        &write_size, &overlapped->write_overlapped);
466 
467     if (!result) {
468         switch (error = GetLastError())
469         {
470         case ERROR_IO_PENDING:
471 #ifndef TUN_ASYNCHRONOUS_WRITES
472             WaitForSingleObject(overlapped->write_event, INFINITE);
473 #endif
474             break;
475         default:
476             return -1;
477         }
478     }
479 
480     return 0;
481 }
482 
tap_win32_thread_entry(LPVOID param)483 static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
484 {
485     tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
486     unsigned long read_size;
487     BOOL result;
488     DWORD dwError;
489     tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
490 
491 
492     for (;;) {
493         result = ReadFile(overlapped->handle,
494                           buffer->buffer,
495                           sizeof(buffer->buffer),
496                           &read_size,
497                           &overlapped->read_overlapped);
498         if (!result) {
499             dwError = GetLastError();
500             if (dwError == ERROR_IO_PENDING) {
501                 WaitForSingleObject(overlapped->read_event, INFINITE);
502                 result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
503                                               &read_size, FALSE);
504                 if (!result) {
505 #ifdef DEBUG_TAP_WIN32
506                     LPVOID lpBuffer;
507                     dwError = GetLastError();
508                     FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
509                                    NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
510                                    (LPTSTR) & lpBuffer, 0, NULL );
511                     fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
512                     LocalFree( lpBuffer );
513 #endif
514                 }
515             } else {
516 #ifdef DEBUG_TAP_WIN32
517                 LPVOID lpBuffer;
518                 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
519                                NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
520                                (LPTSTR) & lpBuffer, 0, NULL );
521                 fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
522                 LocalFree( lpBuffer );
523 #endif
524             }
525         }
526 
527         if(read_size > 0) {
528             buffer->read_size = read_size;
529             put_buffer_on_output_queue(overlapped, buffer);
530             ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL);
531             buffer = get_buffer_from_free_list(overlapped);
532         }
533     }
534 
535     return 0;
536 }
537 
tap_win32_read(tap_win32_overlapped_t * overlapped,uint8_t ** pbuf,int max_size)538 static int tap_win32_read(tap_win32_overlapped_t *overlapped,
539                           uint8_t **pbuf, int max_size)
540 {
541     int size = 0;
542 
543     tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
544 
545     if(buffer != NULL) {
546         *pbuf = buffer->buffer;
547         size = (int)buffer->read_size;
548         if(size > max_size) {
549             size = max_size;
550         }
551     }
552 
553     return size;
554 }
555 
tap_win32_free_buffer(tap_win32_overlapped_t * overlapped,uint8_t * pbuf)556 static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
557                                   uint8_t *pbuf)
558 {
559     tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
560     put_buffer_on_free_list(overlapped, buffer);
561 }
562 
tap_win32_open(tap_win32_overlapped_t ** phandle,const char * prefered_name)563 static int tap_win32_open(tap_win32_overlapped_t **phandle,
564                           const char *prefered_name)
565 {
566     char device_path[256];
567     char device_guid[0x100];
568     int rc;
569     HANDLE handle;
570     BOOL bret;
571     char name_buffer[0x100] = {0, };
572     struct {
573         unsigned long major;
574         unsigned long minor;
575         unsigned long debug;
576     } version;
577     DWORD version_len;
578     DWORD idThread;
579     HANDLE hThread;
580 
581     if (prefered_name != NULL)
582         snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
583 
584     rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
585     if (rc)
586         return -1;
587 
588     snprintf (device_path, sizeof(device_path), "%s%s%s",
589               USERMODEDEVICEDIR,
590               device_guid,
591               TAPSUFFIX);
592 
593     handle = CreateFile (
594         device_path,
595         GENERIC_READ | GENERIC_WRITE,
596         0,
597         0,
598         OPEN_EXISTING,
599         FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
600         0 );
601 
602     if (handle == INVALID_HANDLE_VALUE) {
603         return -1;
604     }
605 
606     bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
607                            &version, sizeof (version),
608                            &version, sizeof (version), &version_len, NULL);
609 
610     if (bret == FALSE) {
611         CloseHandle(handle);
612         return -1;
613     }
614 
615     if (!tap_win32_set_status(handle, TRUE)) {
616         return -1;
617     }
618 
619     tap_win32_overlapped_init(&tap_overlapped, handle);
620 
621     *phandle = &tap_overlapped;
622 
623     hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
624                            (LPVOID)&tap_overlapped, 0, &idThread);
625     return 0;
626 }
627 
628 /********************************************/
629 
630  typedef struct TAPState {
631      VLANClientState *vc;
632      tap_win32_overlapped_t *handle;
633  } TAPState;
634 
tap_cleanup(VLANClientState * vc)635 static void tap_cleanup(VLANClientState *vc)
636 {
637     TAPState *s = vc->opaque;
638 
639     qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL);
640 
641     /* FIXME: need to kill thread and close file handle:
642        tap_win32_close(s);
643     */
644     qemu_free(s);
645 }
646 
tap_receive(VLANClientState * vc,const uint8_t * buf,size_t size)647 static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
648 {
649     TAPState *s = vc->opaque;
650 
651     return tap_win32_write(s->handle, buf, size);
652 }
653 
tap_win32_send(void * opaque)654 static void tap_win32_send(void *opaque)
655 {
656     TAPState *s = opaque;
657     uint8_t *buf;
658     int max_size = 4096;
659     int size;
660 
661     size = tap_win32_read(s->handle, &buf, max_size);
662     if (size > 0) {
663         qemu_send_packet(s->vc, buf, size);
664         tap_win32_free_buffer(s->handle, buf);
665     }
666 }
667 
tap_win32_init(VLANState * vlan,const char * model,const char * name,const char * ifname)668 int tap_win32_init(VLANState *vlan, const char *model,
669                    const char *name, const char *ifname)
670 {
671     TAPState *s;
672 
673     s = qemu_mallocz(sizeof(TAPState));
674     if (!s)
675         return -1;
676     if (tap_win32_open(&s->handle, ifname) < 0) {
677         printf("tap: Could not open '%s'\n", ifname);
678         return -1;
679     }
680 
681     s->vc = qemu_new_vlan_client(vlan, model, name, NULL, tap_receive,
682                                  NULL, tap_cleanup, s);
683 
684     snprintf(s->vc->info_str, sizeof(s->vc->info_str),
685              "tap: ifname=%s", ifname);
686 
687     qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
688     return 0;
689 }
690