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