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 MAC address from tap device */
182 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
183 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
187 if(routing_mode == RMODE_ROUTER) {
191 device_info = "Windows tap device";
193 logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
198 static void enable_device(void) {
199 logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
203 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
205 /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
207 device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
208 device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
210 io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
214 static void disable_device(void) {
215 logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
217 io_del(&device_read_io);
218 CancelIo(device_handle);
220 /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
221 To prevent race conditions, make sure the operation is complete
222 before we close the event it's referencing. */
225 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
226 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
227 if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
228 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
229 device_write_packet.len = 0;
231 CloseHandle(device_read_overlapped.hEvent);
232 CloseHandle(device_write_overlapped.hEvent);
235 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
238 static void close_device(void) {
239 CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
241 free(device); device = NULL;
242 free(iface); iface = NULL;
246 static bool read_packet(vpn_packet_t *packet) {
250 static bool write_packet(vpn_packet_t *packet) {
253 logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
254 packet->len, device_info);
256 if(device_write_packet.len > 0) {
257 /* Make sure the previous write operation is finished before we start the next one;
258 otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
259 which according to MSDN is a no-no. */
261 if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
262 int log_level = (GetLastError() == ERROR_IO_INCOMPLETE) ? DEBUG_TRAFFIC : DEBUG_ALWAYS;
263 logger(log_level, LOG_ERR, "Error while checking previous write to %s %s: %s", device_info, device, winerror(GetLastError()));
268 /* Copy the packet, since the write operation might still be ongoing after we return. */
270 memcpy(&device_write_packet, packet, sizeof *packet);
272 if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped))
273 device_write_packet.len = 0;
274 else if (GetLastError() != ERROR_IO_PENDING) {
275 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
282 const devops_t os_devops = {
283 .setup = setup_device,
284 .close = close_device,
286 .write = write_packet,
287 .enable = enable_device,
288 .disable = disable_device,