2 device.c -- Interaction with Windows tap driver in a MinGW environment
3 Copyright (C) 2002-2005 Ivo Timmermans,
4 2002-2014 Guus Sliepen <guus@tinc-vpn.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "../system.h"
27 #include "../device.h"
28 #include "../logger.h"
33 #include "../xalloc.h"
38 static HANDLE device_handle = INVALID_HANDLE_VALUE;
39 static io_t device_read_io;
40 static OVERLAPPED device_read_overlapped;
41 static OVERLAPPED device_write_overlapped;
42 static vpn_packet_t device_read_packet;
43 static vpn_packet_t device_write_packet;
46 static const char *device_info = "Windows tap device";
50 static void device_issue_read() {
54 ResetEvent(device_read_overlapped.hEvent);
57 status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
60 if(GetLastError() != ERROR_IO_PENDING)
61 logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
62 device, strerror(errno));
67 device_read_packet.len = len;
68 device_read_packet.priority = 0;
69 route(myself, &device_read_packet);
73 static void device_handle_read(void *data, int flags) {
76 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
77 logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
78 device, strerror(errno));
80 if(GetLastError() != ERROR_IO_INCOMPLETE) {
81 /* Must reset event or it will keep firing. */
82 ResetEvent(device_read_overlapped.hEvent);
88 device_read_packet.len = len;
89 device_read_packet.priority = 0;
90 route(myself, &device_read_packet);
94 static bool setup_device(void) {
100 char adaptername[1024];
108 get_config_string(lookup_config(config_tree, "Device"), &device);
109 get_config_string(lookup_config(config_tree, "Interface"), &iface);
111 if(device && iface) {
112 logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
115 /* Open registry and look for network adapters */
117 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
118 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
123 len = sizeof(adapterid);
125 if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
129 /* Find out more about this adapter */
131 snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
133 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
137 len = sizeof(adaptername);
138 err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
147 if(!strcmp(device, adapterid)) {
156 if(!strcmp(iface, adaptername)) {
164 snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
165 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
167 if(device_handle != INVALID_HANDLE_VALUE) {
176 logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
181 device = xstrdup(adapterid);
185 iface = xstrdup(adaptername);
188 /* Try to open the corresponding tap device */
190 if(device_handle == INVALID_HANDLE_VALUE) {
191 snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
192 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
195 if(device_handle == INVALID_HANDLE_VALUE) {
196 logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
200 /* Get version information from tap device */
206 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info), &len, NULL)) {
207 logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
209 logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
211 /* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
212 if(info[0] == 9 && info[1] >= 21)
213 logger(DEBUG_ALWAYS, LOG_WARNING,
214 "You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
215 "Using these drivers with tinc is not recommanded as it can result in poor performance. "
216 "You might want to revert back to 9.0.0.9 instead.");
220 /* Get MAC address from tap device */
222 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
223 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
227 if(routing_mode == RMODE_ROUTER) {
231 device_info = "Windows tap device";
233 logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
235 device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
236 device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
241 static void enable_device(void) {
242 logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
246 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
248 /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
250 io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
254 static void disable_device(void) {
255 logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
257 io_del(&device_read_io);
261 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
263 /* Note that we don't try to cancel ongoing I/O here - we just stop listening.
264 This is because some TAP-Win32 drivers don't seem to handle cancellation very well,
265 especially when combined with other events such as the computer going to sleep - cases
266 were observed where the GetOverlappedResult() would just block indefinitely and never
267 return in that case. */
270 static void close_device(void) {
271 CancelIo(device_handle);
273 /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
274 To prevent race conditions, make sure the operation is complete
275 before we close the event it's referencing. */
279 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
280 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
283 if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
284 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
287 device_write_packet.len = 0;
289 CloseHandle(device_read_overlapped.hEvent);
290 CloseHandle(device_write_overlapped.hEvent);
292 CloseHandle(device_handle);
293 device_handle = INVALID_HANDLE_VALUE;
302 static bool read_packet(vpn_packet_t *packet) {
306 static bool write_packet(vpn_packet_t *packet) {
309 logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
310 packet->len, device_info);
312 if(device_write_packet.len > 0) {
313 /* Make sure the previous write operation is finished before we start the next one;
314 otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
315 which according to MSDN is a no-no. */
317 if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
318 if(GetLastError() != ERROR_IO_INCOMPLETE) {
319 logger(DEBUG_ALWAYS, LOG_ERR, "Error completing previously queued write to %s %s: %s", device_info, device, winerror(GetLastError()));
321 logger(DEBUG_TRAFFIC, LOG_ERR, "Previous overlapped write to %s %s still in progress", device_info, device);
328 /* Copy the packet, since the write operation might still be ongoing after we return. */
330 memcpy(&device_write_packet, packet, sizeof(*packet));
332 ResetEvent(device_write_overlapped.hEvent);
334 if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped)) {
335 // Write was completed immediately.
336 device_write_packet.len = 0;
337 } else if(GetLastError() != ERROR_IO_PENDING) {
338 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
339 device_write_packet.len = 0;
346 const devops_t os_devops = {
347 .setup = setup_device,
348 .close = close_device,
350 .write = write_packet,
351 .enable = enable_device,
352 .disable = disable_device,