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