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 "qemu-error.h" 35 #include <stdio.h> 36 #include <windows.h> 37 #include <winioctl.h> 38 39 //============= 40 // TAP IOCTLs 41 //============= 42 43 #define TAP_CONTROL_CODE(request,method) \ 44 CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) 45 46 #define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) 47 #define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) 48 #define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) 49 #define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) 50 #define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) 51 #define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) 52 #define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) 53 #define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) 54 #define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) 55 56 //================= 57 // Registry keys 58 //================= 59 60 #define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" 61 62 #define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" 63 64 //====================== 65 // Filesystem prefixes 66 //====================== 67 68 #define USERMODEDEVICEDIR "\\\\.\\Global\\" 69 #define TAPSUFFIX ".tap" 70 71 72 //====================== 73 // Compile time configuration 74 //====================== 75 76 //#define DEBUG_TAP_WIN32 77 78 #define TUN_ASYNCHRONOUS_WRITES 1 79 80 #define TUN_BUFFER_SIZE 1560 81 #define TUN_MAX_BUFFER_COUNT 32 82 83 /* 84 * The data member "buffer" must be the first element in the tun_buffer 85 * structure. See the function, tap_win32_free_buffer. 86 */ 87 typedef struct tun_buffer_s { 88 unsigned char buffer [TUN_BUFFER_SIZE]; 89 unsigned long read_size; 90 struct tun_buffer_s* next; 91 } tun_buffer_t; 92 93 typedef struct tap_win32_overlapped { 94 HANDLE handle; 95 HANDLE read_event; 96 HANDLE write_event; 97 HANDLE output_queue_semaphore; 98 HANDLE free_list_semaphore; 99 HANDLE tap_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 110 static tap_win32_overlapped_t tap_overlapped; 111 112 static 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 125 static 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 134 static 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 167 static 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 172 static 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 190 static 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 (LPBYTE)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 (LPBYTE)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 285 static 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 (LPBYTE)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 391 static 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 400 static 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 /* To count buffers, initially no-signal. */ 449 overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL); 450 if(!overlapped->tap_semaphore) 451 fprintf(stderr, "error creating tap_semaphore.\n"); 452 } 453 454 static int tap_win32_write(tap_win32_overlapped_t *overlapped, 455 const void *buffer, unsigned long size) 456 { 457 unsigned long write_size; 458 BOOL result; 459 DWORD error; 460 461 result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped, 462 &write_size, FALSE); 463 464 if (!result && GetLastError() == ERROR_IO_INCOMPLETE) 465 WaitForSingleObject(overlapped->write_event, INFINITE); 466 467 result = WriteFile(overlapped->handle, buffer, size, 468 &write_size, &overlapped->write_overlapped); 469 470 if (!result) { 471 switch (error = GetLastError()) 472 { 473 case ERROR_IO_PENDING: 474 #ifndef TUN_ASYNCHRONOUS_WRITES 475 WaitForSingleObject(overlapped->write_event, INFINITE); 476 #endif 477 break; 478 default: 479 return -1; 480 } 481 } 482 483 return write_size; 484 } 485 486 static DWORD WINAPI tap_win32_thread_entry(LPVOID param) 487 { 488 tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param; 489 unsigned long read_size; 490 BOOL result; 491 DWORD dwError; 492 tun_buffer_t* buffer = get_buffer_from_free_list(overlapped); 493 494 495 for (;;) { 496 result = ReadFile(overlapped->handle, 497 buffer->buffer, 498 sizeof(buffer->buffer), 499 &read_size, 500 &overlapped->read_overlapped); 501 if (!result) { 502 dwError = GetLastError(); 503 if (dwError == ERROR_IO_PENDING) { 504 WaitForSingleObject(overlapped->read_event, INFINITE); 505 result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped, 506 &read_size, FALSE); 507 if (!result) { 508 #ifdef DEBUG_TAP_WIN32 509 LPVOID lpBuffer; 510 dwError = GetLastError(); 511 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 512 NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 513 (LPTSTR) & lpBuffer, 0, NULL ); 514 fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer); 515 LocalFree( lpBuffer ); 516 #endif 517 } 518 } else { 519 #ifdef DEBUG_TAP_WIN32 520 LPVOID lpBuffer; 521 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 522 NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 523 (LPTSTR) & lpBuffer, 0, NULL ); 524 fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer); 525 LocalFree( lpBuffer ); 526 #endif 527 } 528 } 529 530 if(read_size > 0) { 531 buffer->read_size = read_size; 532 put_buffer_on_output_queue(overlapped, buffer); 533 ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL); 534 buffer = get_buffer_from_free_list(overlapped); 535 } 536 } 537 538 return 0; 539 } 540 541 static int tap_win32_read(tap_win32_overlapped_t *overlapped, 542 uint8_t **pbuf, int max_size) 543 { 544 int size = 0; 545 546 tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped); 547 548 if(buffer != NULL) { 549 *pbuf = buffer->buffer; 550 size = (int)buffer->read_size; 551 if(size > max_size) { 552 size = max_size; 553 } 554 } 555 556 return size; 557 } 558 559 static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, 560 uint8_t *pbuf) 561 { 562 tun_buffer_t* buffer = (tun_buffer_t*)pbuf; 563 put_buffer_on_free_list(overlapped, buffer); 564 } 565 566 static int tap_win32_open(tap_win32_overlapped_t **phandle, 567 const char *prefered_name) 568 { 569 char device_path[256]; 570 char device_guid[0x100]; 571 int rc; 572 HANDLE handle; 573 BOOL bret; 574 char name_buffer[0x100] = {0, }; 575 struct { 576 unsigned long major; 577 unsigned long minor; 578 unsigned long debug; 579 } version; 580 DWORD version_len; 581 DWORD idThread; 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 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_OPTIONS_KIND_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(const NetClientOptions *opts, const char *name, 703 VLANState *vlan) 704 { 705 const NetdevTapOptions *tap; 706 707 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); 708 tap = opts->tap; 709 710 if (!tap->has_ifname) { 711 error_report("tap: no interface name"); 712 return -1; 713 } 714 715 if (tap_win32_init(vlan, "tap", name, tap->ifname) == -1) { 716 return -1; 717 } 718 719 return 0; 720 } 721 722 int tap_has_ufo(VLANClientState *vc) 723 { 724 return 0; 725 } 726 727 int tap_has_vnet_hdr(VLANClientState *vc) 728 { 729 return 0; 730 } 731 732 int tap_probe_vnet_hdr_len(int fd, int len) 733 { 734 return 0; 735 } 736 737 void tap_fd_set_vnet_hdr_len(int fd, int len) 738 { 739 } 740 741 void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr) 742 { 743 } 744 745 void tap_set_offload(VLANClientState *vc, int csum, int tso4, 746 int tso6, int ecn, int ufo) 747 { 748 } 749 750 struct vhost_net *tap_get_vhost_net(VLANClientState *nc) 751 { 752 return NULL; 753 } 754