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;
57 status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
59 if (GetLastError() != ERROR_IO_PENDING)
60 logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
61 device, strerror(errno));
65 device_read_packet.len = len;
66 device_read_packet.priority = 0;
67 route(myself, &device_read_packet);
71 static void device_handle_read(void *data, int flags) {
72 ResetEvent(device_read_overlapped.hEvent);
75 if (!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
76 logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
77 device, strerror(errno));
81 device_read_packet.len = len;
82 device_read_packet.priority = 0;
83 route(myself, &device_read_packet);
87 static bool setup_device(void) {
93 char adaptername[1024];
101 get_config_string(lookup_config(config_tree, "Device"), &device);
102 get_config_string(lookup_config(config_tree, "Interface"), &iface);
105 logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
107 /* Open registry and look for network adapters */
109 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
110 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
115 len = sizeof adapterid;
116 if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
119 /* Find out more about this adapter */
121 snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
123 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
126 len = sizeof adaptername;
127 err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
135 if(!strcmp(device, adapterid)) {
143 if(!strcmp(iface, adaptername)) {
150 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
151 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
152 if(device_handle != INVALID_HANDLE_VALUE) {
161 logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
166 device = xstrdup(adapterid);
169 iface = xstrdup(adaptername);
171 /* Try to open the corresponding tap device */
173 if(device_handle == INVALID_HANDLE_VALUE) {
174 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
175 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
178 if(device_handle == INVALID_HANDLE_VALUE) {
179 logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
183 /* Get version information from tap device */
188 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof info, &info, sizeof info, &len, NULL))
189 logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
191 logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
193 /* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
194 if(info[0] == 9 && info[1] >= 21)
195 logger(DEBUG_ALWAYS, LOG_WARNING,
196 "You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
197 "Using these drivers with tinc is not recommanded as it can result in poor performance. "
198 "You might want to revert back to 9.0.0.9 instead.");
202 /* Get MAC address from tap device */
204 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
205 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
209 if(routing_mode == RMODE_ROUTER) {
213 device_info = "Windows tap device";
215 logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
220 static void enable_device(void) {
221 logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
225 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
227 /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
229 device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
230 device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
232 io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
236 static void disable_device(void) {
237 logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
239 io_del(&device_read_io);
240 CancelIo(device_handle);
242 /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
243 To prevent race conditions, make sure the operation is complete
244 before we close the event it's referencing. */
247 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
248 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
249 if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
250 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
251 device_write_packet.len = 0;
253 CloseHandle(device_read_overlapped.hEvent);
254 CloseHandle(device_write_overlapped.hEvent);
257 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
260 static void close_device(void) {
261 CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
263 free(device); device = NULL;
264 free(iface); iface = NULL;
268 static bool read_packet(vpn_packet_t *packet) {
272 static bool write_packet(vpn_packet_t *packet) {
275 logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
276 packet->len, device_info);
278 if(device_write_packet.len > 0) {
279 /* Make sure the previous write operation is finished before we start the next one;
280 otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
281 which according to MSDN is a no-no. */
283 if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
284 int log_level = (GetLastError() == ERROR_IO_INCOMPLETE) ? DEBUG_TRAFFIC : DEBUG_ALWAYS;
285 logger(log_level, LOG_ERR, "Error while checking previous write to %s %s: %s", device_info, device, winerror(GetLastError()));
290 /* Copy the packet, since the write operation might still be ongoing after we return. */
292 memcpy(&device_write_packet, packet, sizeof *packet);
294 if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped))
295 device_write_packet.len = 0;
296 else if (GetLastError() != ERROR_IO_PENDING) {
297 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
304 const devops_t os_devops = {
305 .setup = setup_device,
306 .close = close_device,
308 .write = write_packet,
309 .enable = enable_device,
310 .disable = disable_device,