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