Statistics
| Branch: | Revision:

root / tap-win32.c @ 49dc768d

History | View | Annotate | Download (19.9 kB)

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

    
35
/* NOTE: PCIBus is redefined in winddk.h */
36
#define PCIBus _PCIBus
37
#include <ddk/ntapi.h>
38
#include <ddk/winddk.h>
39
#include <ddk/ntddk.h>
40
#undef PCIBus
41

    
42
//=============
43
// TAP IOCTLs
44
//=============
45

    
46
#define TAP_CONTROL_CODE(request,method) \
47
  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
48

    
49
#define TAP_IOCTL_GET_MAC               TAP_CONTROL_CODE (1, METHOD_BUFFERED)
50
#define TAP_IOCTL_GET_VERSION           TAP_CONTROL_CODE (2, METHOD_BUFFERED)
51
#define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
52
#define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
53
#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
54
#define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
55
#define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
56
#define TAP_IOCTL_GET_LOG_LINE          TAP_CONTROL_CODE (8, METHOD_BUFFERED)
57
#define TAP_IOCTL_CONFIG_DHCP_SET_OPT   TAP_CONTROL_CODE (9, METHOD_BUFFERED)
58

    
59
//=================
60
// Registry keys
61
//=================
62

    
63
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
64

    
65
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
66

    
67
//======================
68
// Filesystem prefixes
69
//======================
70

    
71
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
72
#define TAPSUFFIX         ".tap"
73

    
74

    
75
//======================
76
// Compile time configuration
77
//======================
78

    
79
//#define DEBUG_TAP_WIN32
80

    
81
#define TUN_ASYNCHRONOUS_WRITES 1
82

    
83
#define TUN_BUFFER_SIZE 1560
84
#define TUN_MAX_BUFFER_COUNT 32
85

    
86
/*
87
 * The data member "buffer" must be the first element in the tun_buffer
88
 * structure. See the function, tap_win32_free_buffer.
89
 */
90
typedef struct tun_buffer_s {
91
    unsigned char buffer [TUN_BUFFER_SIZE];
92
    unsigned long read_size;
93
    struct tun_buffer_s* next;
94
} tun_buffer_t;
95

    
96
typedef struct tap_win32_overlapped {
97
    HANDLE handle;
98
    HANDLE read_event;
99
    HANDLE write_event;
100
    HANDLE output_queue_semaphore;
101
    HANDLE free_list_semaphore;
102
    HANDLE tap_semaphore;
103
    CRITICAL_SECTION output_queue_cs;
104
    CRITICAL_SECTION free_list_cs;
105
    OVERLAPPED read_overlapped;
106
    OVERLAPPED write_overlapped;
107
    tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
108
    tun_buffer_t* free_list;
109
    tun_buffer_t* output_queue_front;
110
    tun_buffer_t* output_queue_back;
111
} tap_win32_overlapped_t;
112

    
113
static tap_win32_overlapped_t tap_overlapped;
114

    
115
static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped)
116
{
117
    tun_buffer_t* buffer = NULL;
118
    WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
119
    EnterCriticalSection(&overlapped->free_list_cs);
120
    buffer = overlapped->free_list;
121
//    assert(buffer != NULL);
122
    overlapped->free_list = buffer->next;
123
    LeaveCriticalSection(&overlapped->free_list_cs);
124
    buffer->next = NULL;
125
    return buffer;
126
}
127

    
128
static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
129
{
130
    EnterCriticalSection(&overlapped->free_list_cs);
131
    buffer->next = overlapped->free_list;
132
    overlapped->free_list = buffer;
133
    LeaveCriticalSection(&overlapped->free_list_cs);
134
    ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
135
}
136

    
137
static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block)
138
{
139
    tun_buffer_t* buffer = NULL;
140
    DWORD result, timeout = block ? INFINITE : 0L;
141

    
142
    // Non-blocking call
143
    result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout);
144

    
145
    switch (result)
146
    {
147
        // The semaphore object was signaled.
148
        case WAIT_OBJECT_0:
149
            EnterCriticalSection(&overlapped->output_queue_cs);
150

    
151
            buffer = overlapped->output_queue_front;
152
            overlapped->output_queue_front = buffer->next;
153

    
154
            if(overlapped->output_queue_front == NULL) {
155
                overlapped->output_queue_back = NULL;
156
            }
157

    
158
            LeaveCriticalSection(&overlapped->output_queue_cs);
159
            break;
160

    
161
        // Semaphore was nonsignaled, so a time-out occurred.
162
        case WAIT_TIMEOUT:
163
            // Cannot open another window.
164
            break;
165
    }
166

    
167
    return buffer;
168
}
169

    
170
static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped)
171
{
172
    return get_buffer_from_output_queue(overlapped, 0);
173
}
174

    
175
static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
176
{
177
    EnterCriticalSection(&overlapped->output_queue_cs);
178

    
179
    if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
180
        overlapped->output_queue_front = overlapped->output_queue_back = buffer;
181
    } else {
182
        buffer->next = NULL;
183
        overlapped->output_queue_back->next = buffer;
184
        overlapped->output_queue_back = buffer;
185
    }
186

    
187
    LeaveCriticalSection(&overlapped->output_queue_cs);
188

    
189
    ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
190
}
191

    
192

    
193
static int is_tap_win32_dev(const char *guid)
194
{
195
    HKEY netcard_key;
196
    LONG status;
197
    DWORD len;
198
    int i = 0;
199

    
200
    status = RegOpenKeyEx(
201
        HKEY_LOCAL_MACHINE,
202
        ADAPTER_KEY,
203
        0,
204
        KEY_READ,
205
        &netcard_key);
206

    
207
    if (status != ERROR_SUCCESS) {
208
        return FALSE;
209
    }
210

    
211
    for (;;) {
212
        char enum_name[256];
213
        char unit_string[256];
214
        HKEY unit_key;
215
        char component_id_string[] = "ComponentId";
216
        char component_id[256];
217
        char net_cfg_instance_id_string[] = "NetCfgInstanceId";
218
        char net_cfg_instance_id[256];
219
        DWORD data_type;
220

    
221
        len = sizeof (enum_name);
222
        status = RegEnumKeyEx(
223
            netcard_key,
224
            i,
225
            enum_name,
226
            &len,
227
            NULL,
228
            NULL,
229
            NULL,
230
            NULL);
231

    
232
        if (status == ERROR_NO_MORE_ITEMS)
233
            break;
234
        else if (status != ERROR_SUCCESS) {
235
            return FALSE;
236
        }
237

    
238
        snprintf (unit_string, sizeof(unit_string), "%s\\%s",
239
                  ADAPTER_KEY, enum_name);
240

    
241
        status = RegOpenKeyEx(
242
            HKEY_LOCAL_MACHINE,
243
            unit_string,
244
            0,
245
            KEY_READ,
246
            &unit_key);
247

    
248
        if (status != ERROR_SUCCESS) {
249
            return FALSE;
250
        } else {
251
            len = sizeof (component_id);
252
            status = RegQueryValueEx(
253
                unit_key,
254
                component_id_string,
255
                NULL,
256
                &data_type,
257
                component_id,
258
                &len);
259

    
260
            if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
261
                len = sizeof (net_cfg_instance_id);
262
                status = RegQueryValueEx(
263
                    unit_key,
264
                    net_cfg_instance_id_string,
265
                    NULL,
266
                    &data_type,
267
                    net_cfg_instance_id,
268
                    &len);
269

    
270
                if (status == ERROR_SUCCESS && data_type == REG_SZ) {
271
                    if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
272
                        !strcmp (net_cfg_instance_id, guid)) {
273
                        RegCloseKey (unit_key);
274
                        RegCloseKey (netcard_key);
275
                        return TRUE;
276
                    }
277
                }
278
            }
279
            RegCloseKey (unit_key);
280
        }
281
        ++i;
282
    }
283

    
284
    RegCloseKey (netcard_key);
285
    return FALSE;
286
}
287

    
288
static int get_device_guid(
289
    char *name,
290
    int name_size,
291
    char *actual_name,
292
    int actual_name_size)
293
{
294
    LONG status;
295
    HKEY control_net_key;
296
    DWORD len;
297
    int i = 0;
298
    int stop = 0;
299

    
300
    status = RegOpenKeyEx(
301
        HKEY_LOCAL_MACHINE,
302
        NETWORK_CONNECTIONS_KEY,
303
        0,
304
        KEY_READ,
305
        &control_net_key);
306

    
307
    if (status != ERROR_SUCCESS) {
308
        return -1;
309
    }
310

    
311
    while (!stop)
312
    {
313
        char enum_name[256];
314
        char connection_string[256];
315
        HKEY connection_key;
316
        char name_data[256];
317
        DWORD name_type;
318
        const char name_string[] = "Name";
319

    
320
        len = sizeof (enum_name);
321
        status = RegEnumKeyEx(
322
            control_net_key,
323
            i,
324
            enum_name,
325
            &len,
326
            NULL,
327
            NULL,
328
            NULL,
329
            NULL);
330

    
331
        if (status == ERROR_NO_MORE_ITEMS)
332
            break;
333
        else if (status != ERROR_SUCCESS) {
334
            return -1;
335
        }
336

    
337
        snprintf(connection_string,
338
             sizeof(connection_string),
339
             "%s\\%s\\Connection",
340
             NETWORK_CONNECTIONS_KEY, enum_name);
341

    
342
        status = RegOpenKeyEx(
343
            HKEY_LOCAL_MACHINE,
344
            connection_string,
345
            0,
346
            KEY_READ,
347
            &connection_key);
348

    
349
        if (status == ERROR_SUCCESS) {
350
            len = sizeof (name_data);
351
            status = RegQueryValueEx(
352
                connection_key,
353
                name_string,
354
                NULL,
355
                &name_type,
356
                name_data,
357
                &len);
358

    
359
            if (status != ERROR_SUCCESS || name_type != REG_SZ) {
360
                    return -1;
361
            }
362
            else {
363
                if (is_tap_win32_dev(enum_name)) {
364
                    snprintf(name, name_size, "%s", enum_name);
365
                    if (actual_name) {
366
                        if (strcmp(actual_name, "") != 0) {
367
                            if (strcmp(name_data, actual_name) != 0) {
368
                                RegCloseKey (connection_key);
369
                                ++i;
370
                                continue;
371
                            }
372
                        }
373
                        else {
374
                            snprintf(actual_name, actual_name_size, "%s", name_data);
375
                        }
376
                    }
377
                    stop = 1;
378
                }
379
            }
380

    
381
            RegCloseKey (connection_key);
382
        }
383
        ++i;
384
    }
385

    
386
    RegCloseKey (control_net_key);
387

    
388
    if (stop == 0)
389
        return -1;
390

    
391
    return 0;
392
}
393

    
394
static int tap_win32_set_status(HANDLE handle, int status)
395
{
396
    unsigned long len = 0;
397

    
398
    return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
399
                &status, sizeof (status),
400
                &status, sizeof (status), &len, NULL);
401
}
402

    
403
static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
404
{
405
    overlapped->handle = handle;
406

    
407
    overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
408
    overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
409

    
410
    overlapped->read_overlapped.Offset = 0;
411
    overlapped->read_overlapped.OffsetHigh = 0;
412
    overlapped->read_overlapped.hEvent = overlapped->read_event;
413

    
414
    overlapped->write_overlapped.Offset = 0;
415
    overlapped->write_overlapped.OffsetHigh = 0;
416
    overlapped->write_overlapped.hEvent = overlapped->write_event;
417

    
418
    InitializeCriticalSection(&overlapped->output_queue_cs);
419
    InitializeCriticalSection(&overlapped->free_list_cs);
420

    
421
    overlapped->output_queue_semaphore = CreateSemaphore(
422
        NULL,   // default security attributes
423
        0,   // initial count
424
        TUN_MAX_BUFFER_COUNT,   // maximum count
425
        NULL);  // unnamed semaphore
426

    
427
    if(!overlapped->output_queue_semaphore)  {
428
        fprintf(stderr, "error creating output queue semaphore!\n");
429
    }
430

    
431
    overlapped->free_list_semaphore = CreateSemaphore(
432
        NULL,   // default security attributes
433
        TUN_MAX_BUFFER_COUNT,   // initial count
434
        TUN_MAX_BUFFER_COUNT,   // maximum count
435
        NULL);  // unnamed semaphore
436

    
437
    if(!overlapped->free_list_semaphore)  {
438
        fprintf(stderr, "error creating free list semaphore!\n");
439
    }
440

    
441
    overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
442

    
443
    {
444
        unsigned index;
445
        for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
446
            tun_buffer_t* element = &overlapped->buffers[index];
447
            element->next = overlapped->free_list;
448
            overlapped->free_list = element;
449
        }
450
    }
451
    /* To count buffers, initially no-signal. */
452
    overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL);
453
    if(!overlapped->tap_semaphore)
454
        fprintf(stderr, "error creating tap_semaphore.\n");
455
}
456

    
457
static int tap_win32_write(tap_win32_overlapped_t *overlapped,
458
                           const void *buffer, unsigned long size)
459
{
460
    unsigned long write_size;
461
    BOOL result;
462
    DWORD error;
463

    
464
    result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
465
                                  &write_size, FALSE);
466

    
467
    if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
468
        WaitForSingleObject(overlapped->write_event, INFINITE);
469

    
470
    result = WriteFile(overlapped->handle, buffer, size,
471
                       &write_size, &overlapped->write_overlapped);
472

    
473
    if (!result) {
474
        switch (error = GetLastError())
475
        {
476
        case ERROR_IO_PENDING:
477
#ifndef TUN_ASYNCHRONOUS_WRITES
478
            WaitForSingleObject(overlapped->write_event, INFINITE);
479
#endif
480
            break;
481
        default:
482
            return -1;
483
        }
484
    }
485

    
486
    return 0;
487
}
488

    
489
static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
490
{
491
    tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
492
    unsigned long read_size;
493
    BOOL result;
494
    DWORD dwError;
495
    tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
496

    
497

    
498
    for (;;) {
499
        result = ReadFile(overlapped->handle,
500
                          buffer->buffer,
501
                          sizeof(buffer->buffer),
502
                          &read_size,
503
                          &overlapped->read_overlapped);
504
        if (!result) {
505
            dwError = GetLastError();
506
            if (dwError == ERROR_IO_PENDING) {
507
                WaitForSingleObject(overlapped->read_event, INFINITE);
508
                result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
509
                                              &read_size, FALSE);
510
                if (!result) {
511
#ifdef DEBUG_TAP_WIN32
512
                    LPVOID lpBuffer;
513
                    dwError = GetLastError();
514
                    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
515
                                   NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
516
                                   (LPTSTR) & lpBuffer, 0, NULL );
517
                    fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
518
                    LocalFree( lpBuffer );
519
#endif
520
                }
521
            } else {
522
#ifdef DEBUG_TAP_WIN32
523
                LPVOID lpBuffer;
524
                FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
525
                               NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
526
                               (LPTSTR) & lpBuffer, 0, NULL );
527
                fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
528
                LocalFree( lpBuffer );
529
#endif
530
            }
531
        }
532

    
533
        if(read_size > 0) {
534
            buffer->read_size = read_size;
535
            put_buffer_on_output_queue(overlapped, buffer);
536
            ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL);
537
            buffer = get_buffer_from_free_list(overlapped);
538
        }
539
    }
540

    
541
    return 0;
542
}
543

    
544
static int tap_win32_read(tap_win32_overlapped_t *overlapped,
545
                          uint8_t **pbuf, int max_size)
546
{
547
    int size = 0;
548

    
549
    tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
550

    
551
    if(buffer != NULL) {
552
        *pbuf = buffer->buffer;
553
        size = (int)buffer->read_size;
554
        if(size > max_size) {
555
            size = max_size;
556
        }
557
    }
558

    
559
    return size;
560
}
561

    
562
static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
563
                                  char* pbuf)
564
{
565
    tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
566
    put_buffer_on_free_list(overlapped, buffer);
567
}
568

    
569
static int tap_win32_open(tap_win32_overlapped_t **phandle,
570
                          const char *prefered_name)
571
{
572
    char device_path[256];
573
    char device_guid[0x100];
574
    int rc;
575
    HANDLE handle;
576
    BOOL bret;
577
    char name_buffer[0x100] = {0, };
578
    struct {
579
        unsigned long major;
580
        unsigned long minor;
581
        unsigned long debug;
582
    } version;
583
    LONG version_len;
584
    DWORD idThread;
585
    HANDLE hThread;
586

    
587
    if (prefered_name != NULL)
588
        snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
589

    
590
    rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
591
    if (rc)
592
        return -1;
593

    
594
    snprintf (device_path, sizeof(device_path), "%s%s%s",
595
              USERMODEDEVICEDIR,
596
              device_guid,
597
              TAPSUFFIX);
598

    
599
    handle = CreateFile (
600
        device_path,
601
        GENERIC_READ | GENERIC_WRITE,
602
        0,
603
        0,
604
        OPEN_EXISTING,
605
        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
606
        0 );
607

    
608
    if (handle == INVALID_HANDLE_VALUE) {
609
        return -1;
610
    }
611

    
612
    bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
613
                           &version, sizeof (version),
614
                           &version, sizeof (version), &version_len, NULL);
615

    
616
    if (bret == FALSE) {
617
        CloseHandle(handle);
618
        return -1;
619
    }
620

    
621
    if (!tap_win32_set_status(handle, TRUE)) {
622
        return -1;
623
    }
624

    
625
    tap_win32_overlapped_init(&tap_overlapped, handle);
626

    
627
    *phandle = &tap_overlapped;
628

    
629
    hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
630
                           (LPVOID)&tap_overlapped, 0, &idThread);
631
    return 0;
632
}
633

    
634
/********************************************/
635

    
636
 typedef struct TAPState {
637
     VLANClientState *vc;
638
     tap_win32_overlapped_t *handle;
639
 } TAPState;
640

    
641
static void tap_receive(void *opaque, const uint8_t *buf, int size)
642
{
643
    TAPState *s = opaque;
644

    
645
    tap_win32_write(s->handle, buf, size);
646
}
647

    
648
static void tap_win32_send(void *opaque)
649
{
650
    TAPState *s = opaque;
651
    uint8_t *buf;
652
    int max_size = 4096;
653
    int size;
654

    
655
    size = tap_win32_read(s->handle, &buf, max_size);
656
    if (size > 0) {
657
        qemu_send_packet(s->vc, buf, size);
658
        tap_win32_free_buffer(s->handle, buf);
659
    }
660
}
661

    
662
int tap_win32_init(VLANState *vlan, const char *model,
663
                   const char *name, const char *ifname)
664
{
665
    TAPState *s;
666

    
667
    s = qemu_mallocz(sizeof(TAPState));
668
    if (!s)
669
        return -1;
670
    if (tap_win32_open(&s->handle, ifname) < 0) {
671
        printf("tap: Could not open '%s'\n", ifname);
672
        return -1;
673
    }
674

    
675
    s->vc = qemu_new_vlan_client(vlan, model, name, tap_receive, NULL, s);
676

    
677
    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
678
             "tap: ifname=%s", ifname);
679

    
680
    qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
681
    return 0;
682
}