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