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);
104 /* Open registry and look for network adapters */
106 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
107 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
112 len = sizeof adapterid;
113 if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
116 /* Find out more about this adapter */
118 snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
120 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
123 len = sizeof adaptername;
124 err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
132 if(!strcmp(device, adapterid)) {
140 if(!strcmp(iface, adaptername)) {
147 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
148 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
149 if(device_handle != INVALID_HANDLE_VALUE) {
158 logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
163 device = xstrdup(adapterid);
166 iface = xstrdup(adaptername);
168 /* Try to open the corresponding tap device */
170 if(device_handle == INVALID_HANDLE_VALUE) {
171 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
172 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
175 if(device_handle == INVALID_HANDLE_VALUE) {
176 logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
180 /* Get version information from tap device */
185 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof info, &info, sizeof info, &len, NULL))
186 logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
188 logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
190 /* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
191 if(info[0] == 9 && info[1] >= 21)
192 logger(DEBUG_ALWAYS, LOG_WARNING,
193 "You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
194 "Using these drivers with tinc is not recommanded as it can result in poor performance. "
195 "You might want to revert back to 9.0.0.9 instead.");
199 /* Get MAC address from tap device */
201 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
202 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
206 if(routing_mode == RMODE_ROUTER) {
210 device_info = "Windows tap device";
212 logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
217 static void enable_device(void) {
218 logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
222 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
224 /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
226 device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
227 device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
229 io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
233 static void disable_device(void) {
234 logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
236 io_del(&device_read_io);
237 CancelIo(device_handle);
239 /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
240 To prevent race conditions, make sure the operation is complete
241 before we close the event it's referencing. */
244 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
245 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
246 if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
247 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
248 device_write_packet.len = 0;
250 CloseHandle(device_read_overlapped.hEvent);
251 CloseHandle(device_write_overlapped.hEvent);
254 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
257 static void close_device(void) {
258 CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
260 free(device); device = NULL;
261 free(iface); iface = NULL;
265 static bool read_packet(vpn_packet_t *packet) {
269 static bool write_packet(vpn_packet_t *packet) {
272 logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
273 packet->len, device_info);
275 if(device_write_packet.len > 0) {
276 /* Make sure the previous write operation is finished before we start the next one;
277 otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
278 which according to MSDN is a no-no. */
280 if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
281 int log_level = (GetLastError() == ERROR_IO_INCOMPLETE) ? DEBUG_TRAFFIC : DEBUG_ALWAYS;
282 logger(log_level, LOG_ERR, "Error while checking previous write to %s %s: %s", device_info, device, winerror(GetLastError()));
287 /* Copy the packet, since the write operation might still be ongoing after we return. */
289 memcpy(&device_write_packet, packet, sizeof *packet);
291 if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped))
292 device_write_packet.len = 0;
293 else if (GetLastError() != ERROR_IO_PENDING) {
294 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
301 const devops_t os_devops = {
302 .setup = setup_device,
303 .close = close_device,
305 .write = write_packet,
306 .enable = enable_device,
307 .disable = disable_device,