2 device.c -- Interaction with Windows tap driver in a Cygwin 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"
23 #include <w32api/windows.h>
24 #include <w32api/winioctl.h>
27 #include "../device.h"
28 #include "../logger.h"
33 #include "../xalloc.h"
35 #include "../mingw/common.h"
38 static HANDLE device_handle
= INVALID_HANDLE_VALUE
;
41 static char *device_info
= NULL
;
43 static pid_t reader_pid
;
46 static bool setup_device(void) {
52 char adaptername
[1024];
59 get_config_string(lookup_config(config_tree
, "Device"), &device
);
60 get_config_string(lookup_config(config_tree
, "Interface"), &iface
);
62 /* Open registry and look for network adapters */
64 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE
, NETWORK_CONNECTIONS_KEY
, 0, KEY_READ
, &key
)) {
65 logger(DEBUG_ALWAYS
, LOG_ERR
, "Unable to read registry: %s", winerror(GetLastError()));
70 len
= sizeof adapterid
;
71 if(RegEnumKeyEx(key
, i
, adapterid
, &len
, 0, 0, 0, NULL
))
74 /* Find out more about this adapter */
76 snprintf(regpath
, sizeof regpath
, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY
, adapterid
);
78 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE
, regpath
, 0, KEY_READ
, &key2
))
81 len
= sizeof adaptername
;
82 err
= RegQueryValueEx(key2
, "Name", 0, 0, adaptername
, &len
);
90 if(!strcmp(device
, adapterid
)) {
98 if(!strcmp(iface
, adaptername
)) {
105 snprintf(tapname
, sizeof tapname
, USERMODEDEVICEDIR
"%s" TAPSUFFIX
, adapterid
);
106 device_handle
= CreateFile(tapname
, GENERIC_WRITE
| GENERIC_READ
, 0, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_SYSTEM
, 0);
107 if(device_handle
!= INVALID_HANDLE_VALUE
) {
108 CloseHandle(device_handle
);
117 logger(DEBUG_ALWAYS
, LOG_ERR
, "No Windows tap device found!");
122 device
= xstrdup(adapterid
);
125 iface
= xstrdup(adaptername
);
127 snprintf(tapname
, sizeof tapname
, USERMODEDEVICEDIR
"%s" TAPSUFFIX
, device
);
129 /* Now we are going to open this device twice: once for reading and once for writing.
130 We do this because apparently it isn't possible to check for activity in the select() loop.
131 Furthermore I don't really know how to do it the "Windows" way. */
133 if(socketpair(AF_UNIX
, SOCK_DGRAM
, PF_UNIX
, sp
)) {
134 logger(DEBUG_ALWAYS
, LOG_DEBUG
, "System call `%s' failed: %s", "socketpair", strerror(errno
));
138 /* The parent opens the tap device for writing. */
140 device_handle
= CreateFile(tapname
, GENERIC_WRITE
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_SYSTEM
, 0);
142 if(device_handle
== INVALID_HANDLE_VALUE
) {
143 logger(DEBUG_ALWAYS
, LOG_ERR
, "Could not open Windows tap device %s (%s) for writing: %s", device
, iface
, winerror(GetLastError()));
149 /* Get MAC address from tap device */
151 if(!DeviceIoControl(device_handle
, TAP_IOCTL_GET_MAC
, mymac
.x
, sizeof mymac
.x
, mymac
.x
, sizeof mymac
.x
, &len
, 0)) {
152 logger(DEBUG_ALWAYS
, LOG_ERR
, "Could not get MAC address from Windows tap device %s (%s): %s", device
, iface
, winerror(GetLastError()));
156 if(routing_mode
== RMODE_ROUTER
) {
160 /* Now we start the child */
164 if(reader_pid
== -1) {
165 logger(DEBUG_ALWAYS
, LOG_DEBUG
, "System call `%s' failed: %s", "fork", strerror(errno
));
170 /* The child opens the tap device for reading, blocking.
171 It passes everything it reads to the socket. */
176 CloseHandle(device_handle
);
178 device_handle
= CreateFile(tapname
, GENERIC_READ
, FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_SYSTEM
, 0);
180 if(device_handle
== INVALID_HANDLE_VALUE
) {
181 logger(DEBUG_ALWAYS
, LOG_ERR
, "Could not open Windows tap device %s (%s) for reading: %s", device
, iface
, winerror(GetLastError()));
183 write(sp
[1], buf
, 1);
187 logger(DEBUG_ALWAYS
, LOG_DEBUG
, "Tap reader forked and running.");
192 write(sp
[1], buf
, 1);
197 ReadFile(device_handle
, buf
, MTU
, &inlen
, NULL
);
198 write(sp
[1], buf
, inlen
);
202 read(device_fd
, &gelukt
, 1);
204 logger(DEBUG_ALWAYS
, LOG_DEBUG
, "Tap reader failed!");
208 device_info
= "Windows tap device";
210 logger(DEBUG_ALWAYS
, LOG_INFO
, "%s (%s) is a %s", device
, iface
, device_info
);
215 static void close_device(void) {
218 CloseHandle(device_handle
); device_handle
= INVALID_HANDLE_VALUE
;
220 kill(reader_pid
, SIGKILL
);
222 free(device
); device
= NULL
;
223 free(iface
); iface
= NULL
;
227 static bool read_packet(vpn_packet_t
*packet
) {
230 if((inlen
= read(sp
[0], DATA(packet
), MTU
)) <= 0) {
231 logger(DEBUG_ALWAYS
, LOG_ERR
, "Error while reading from %s %s: %s", device_info
,
232 device
, strerror(errno
));
238 logger(DEBUG_TRAFFIC
, LOG_DEBUG
, "Read packet of %d bytes from %s", packet
->len
,
244 static bool write_packet(vpn_packet_t
*packet
) {
247 logger(DEBUG_TRAFFIC
, LOG_DEBUG
, "Writing packet of %d bytes to %s",
248 packet
->len
, device_info
);
250 if(!WriteFile (device_handle
, DATA(packet
), packet
->len
, &outlen
, NULL
)) {
251 logger(DEBUG_ALWAYS
, LOG_ERR
, "Error while writing to %s %s: %s", device_info
, device
, winerror(GetLastError()));
258 const devops_t os_devops
= {
259 .setup
= setup_device
,
260 .close
= close_device
,
262 .write
= write_packet
,