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