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 char *device_info = NULL;
50 static void device_issue_read() {
51 device_read_overlapped.Offset = 0;
52 device_read_overlapped.OffsetHigh = 0;
58 status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
61 if(GetLastError() != ERROR_IO_PENDING)
62 logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
63 device, strerror(errno));
68 device_read_packet.len = len;
69 device_read_packet.priority = 0;
70 route(myself, &device_read_packet);
74 static void device_handle_read(void *data, int flags) {
75 ResetEvent(device_read_overlapped.hEvent);
79 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
80 logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
81 device, strerror(errno));
85 device_read_packet.len = len;
86 device_read_packet.priority = 0;
87 route(myself, &device_read_packet);
91 static bool setup_device(void) {
97 char adaptername[1024];
105 get_config_string(lookup_config(config_tree, "Device"), &device);
106 get_config_string(lookup_config(config_tree, "Interface"), &iface);
108 if(device && iface) {
109 logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
112 /* Open registry and look for network adapters */
114 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
115 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
120 len = sizeof(adapterid);
122 if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
126 /* Find out more about this adapter */
128 snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
130 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
134 len = sizeof(adaptername);
135 err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
144 if(!strcmp(device, adapterid)) {
153 if(!strcmp(iface, adaptername)) {
161 snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
162 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
164 if(device_handle != INVALID_HANDLE_VALUE) {
173 logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
178 device = xstrdup(adapterid);
182 iface = xstrdup(adaptername);
185 /* Try to open the corresponding tap device */
187 if(device_handle == INVALID_HANDLE_VALUE) {
188 snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
189 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
192 if(device_handle == INVALID_HANDLE_VALUE) {
193 logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
197 /* Get version information from tap device */
203 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info), &len, NULL)) {
204 logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
206 logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
208 /* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
209 if(info[0] == 9 && info[1] >= 21)
210 logger(DEBUG_ALWAYS, LOG_WARNING,
211 "You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
212 "Using these drivers with tinc is not recommanded as it can result in poor performance. "
213 "You might want to revert back to 9.0.0.9 instead.");
217 /* Get MAC address from tap device */
219 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
220 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
224 if(routing_mode == RMODE_ROUTER) {
228 device_info = "Windows tap device";
230 logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
232 device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
233 device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
238 static void enable_device(void) {
239 logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
243 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
245 /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
247 io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
251 static void disable_device(void) {
252 logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
254 io_del(&device_read_io);
258 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
260 /* Note that we don't try to cancel ongoing I/O here - we just stop listening.
261 This is because some TAP-Win32 drivers don't seem to handle cancellation very well,
262 especially when combined with other events such as the computer going to sleep - cases
263 were observed where the GetOverlappedResult() would just block indefinitely and never
264 return in that case. */
267 static void close_device(void) {
268 CancelIo(device_handle);
270 /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
271 To prevent race conditions, make sure the operation is complete
272 before we close the event it's referencing. */
276 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
277 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
280 if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
281 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
284 device_write_packet.len = 0;
286 CloseHandle(device_read_overlapped.hEvent);
287 CloseHandle(device_write_overlapped.hEvent);
289 CloseHandle(device_handle);
290 device_handle = INVALID_HANDLE_VALUE;
299 static bool read_packet(vpn_packet_t *packet) {
303 static bool write_packet(vpn_packet_t *packet) {
306 logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
307 packet->len, device_info);
309 if(device_write_packet.len > 0) {
310 /* Make sure the previous write operation is finished before we start the next one;
311 otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
312 which according to MSDN is a no-no. */
314 if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
315 int log_level = (GetLastError() == ERROR_IO_INCOMPLETE) ? DEBUG_TRAFFIC : DEBUG_ALWAYS;
316 logger(log_level, LOG_ERR, "Error while checking previous write to %s %s: %s", device_info, device, winerror(GetLastError()));
321 /* Copy the packet, since the write operation might still be ongoing after we return. */
323 memcpy(&device_write_packet, packet, sizeof(*packet));
325 if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped)) {
326 device_write_packet.len = 0;
327 } else if(GetLastError() != ERROR_IO_PENDING) {
328 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
335 const devops_t os_devops = {
336 .setup = setup_device,
337 .close = close_device,
339 .write = write_packet,
340 .enable = enable_device,
341 .disable = disable_device,