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 vpn_packet_t device_read_packet;
44 static char *device_info = NULL;
48 static void device_issue_read() {
49 device_read_overlapped.Offset = 0;
50 device_read_overlapped.OffsetHigh = 0;
55 status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
57 if (GetLastError() != ERROR_IO_PENDING)
58 logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
59 device, strerror(errno));
63 device_read_packet.len = len;
64 device_read_packet.priority = 0;
65 route(myself, &device_read_packet);
69 static void device_handle_read(void *data, int flags) {
70 ResetEvent(device_read_overlapped.hEvent);
73 if (!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
74 logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
75 device, strerror(errno));
79 device_read_packet.len = len;
80 device_read_packet.priority = 0;
81 route(myself, &device_read_packet);
85 static bool setup_device(void) {
91 char adaptername[1024];
99 get_config_string(lookup_config(config_tree, "Device"), &device);
100 get_config_string(lookup_config(config_tree, "Interface"), &iface);
102 /* Open registry and look for network adapters */
104 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
105 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
110 len = sizeof adapterid;
111 if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
114 /* Find out more about this adapter */
116 snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
118 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
121 len = sizeof adaptername;
122 err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
130 if(!strcmp(device, adapterid)) {
138 if(!strcmp(iface, adaptername)) {
145 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
146 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
147 if(device_handle != INVALID_HANDLE_VALUE) {
156 logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
161 device = xstrdup(adapterid);
164 iface = xstrdup(adaptername);
166 /* Try to open the corresponding tap device */
168 if(device_handle == INVALID_HANDLE_VALUE) {
169 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
170 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
173 if(device_handle == INVALID_HANDLE_VALUE) {
174 logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
178 /* Get MAC address from tap device */
180 if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
181 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
185 if(routing_mode == RMODE_ROUTER) {
189 device_info = "Windows tap device";
191 logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
196 static void enable_device(void) {
197 logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
201 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
203 io_add_event(&device_read_io, device_handle_read, NULL, CreateEvent(NULL, TRUE, FALSE, NULL));
204 device_read_overlapped.hEvent = device_read_io.event;
208 static void disable_device(void) {
209 logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
211 io_del(&device_read_io);
212 CancelIo(device_handle);
214 /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
215 To prevent race conditions, make sure the operation is complete
216 before we close the event it's referencing. */
219 if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED)
220 logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
222 CloseHandle(device_read_overlapped.hEvent);
225 DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
228 static void close_device(void) {
229 CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
231 free(device); device = NULL;
232 free(iface); iface = NULL;
236 static bool read_packet(vpn_packet_t *packet) {
240 static bool write_packet(vpn_packet_t *packet) {
242 OVERLAPPED overlapped = {0};
244 logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
245 packet->len, device_info);
247 if(!WriteFile(device_handle, DATA(packet), packet->len, &outlen, &overlapped)) {
248 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
255 const devops_t os_devops = {
256 .setup = setup_device,
257 .close = close_device,
259 .write = write_packet,
260 .enable = enable_device,
261 .disable = disable_device,