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