33b13da60b89a9496f311c50f48757af9764ded7
[tinc] / src / mingw / device.c
1 /*
2     device.c -- Interaction with Windows tap driver in a MinGW environment
3     Copyright (C) 2002-2005 Ivo Timmermans,
4                   2002-2013 Guus Sliepen <guus@tinc-vpn.org>
5
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.
10
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.
15
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.
19 */
20
21 #include "../system.h"
22
23 #include <windows.h>
24 #include <winioctl.h>
25
26 #include "../conf.h"
27 #include "../device.h"
28 #include "../logger.h"
29 #include "../names.h"
30 #include "../net.h"
31 #include "../route.h"
32 #include "../utils.h"
33 #include "../xalloc.h"
34
35 #include "common.h"
36
37 int device_fd = -1;
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;
42 char *device = NULL;
43 char *iface = NULL;
44 static char *device_info = NULL;
45
46 static uint64_t device_total_in = 0;
47 static uint64_t device_total_out = 0;
48
49 extern char *myport;
50
51 static void device_issue_read() {
52         device_read_overlapped.Offset = 0;
53         device_read_overlapped.OffsetHigh = 0;
54
55         int status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, NULL, &device_read_overlapped);
56
57         if(!status && GetLastError() != ERROR_IO_PENDING) {
58                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
59                            device, strerror(errno));
60         }
61 }
62
63 static void device_handle_read(void *data) {
64         ResetEvent(device_read_overlapped.hEvent);
65
66         DWORD len;
67         if (!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
68                 logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
69                            device, strerror(errno));
70                 return;
71         }
72
73         device_read_packet.len = len;
74         device_read_packet.priority = 0;
75         route(myself, &device_read_packet);
76         device_issue_read();
77 }
78
79 static bool setup_device(void) {
80         HKEY key, key2;
81         int i;
82
83         char regpath[1024];
84         char adapterid[1024];
85         char adaptername[1024];
86         char tapname[1024];
87         DWORD len;
88
89         bool found = false;
90
91         int err;
92         HANDLE thread;
93
94         get_config_string(lookup_config(config_tree, "Device"), &device);
95         get_config_string(lookup_config(config_tree, "Interface"), &iface);
96
97         /* Open registry and look for network adapters */
98
99         if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
100                 logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
101                 return false;
102         }
103
104         for (i = 0; ; i++) {
105                 len = sizeof adapterid;
106                 if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
107                         break;
108
109                 /* Find out more about this adapter */
110
111                 snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
112
113                 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
114                         continue;
115
116                 len = sizeof adaptername;
117                 err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
118
119                 RegCloseKey(key2);
120
121                 if(err)
122                         continue;
123
124                 if(device) {
125                         if(!strcmp(device, adapterid)) {
126                                 found = true;
127                                 break;
128                         } else
129                                 continue;
130                 }
131
132                 if(iface) {
133                         if(!strcmp(iface, adaptername)) {
134                                 found = true;
135                                 break;
136                         } else
137                                 continue;
138                 }
139
140                 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
141                 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
142                 if(device_handle != INVALID_HANDLE_VALUE) {
143                         found = true;
144                         break;
145                 }
146         }
147
148         RegCloseKey(key);
149
150         if(!found) {
151                 logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
152                 return false;
153         }
154
155         if(!device)
156                 device = xstrdup(adapterid);
157
158         if(!iface)
159                 iface = xstrdup(adaptername);
160
161         /* Try to open the corresponding tap device */
162
163         if(device_handle == INVALID_HANDLE_VALUE) {
164                 snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
165                 device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
166         }
167
168         if(device_handle == INVALID_HANDLE_VALUE) {
169                 logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
170                 return false;
171         }
172
173         /* Get MAC address from tap device */
174
175         if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
176                 logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
177                 return false;
178         }
179
180         if(routing_mode == RMODE_ROUTER) {
181                 overwrite_mac = 1;
182         }
183
184         /* Start the tap reader */
185
186         io_add_event(&device_read_io, device_handle_read, NULL, CreateEvent(NULL, TRUE, FALSE, NULL));
187         device_read_overlapped.hEvent = device_read_io.event;
188         device_issue_read();
189
190         device_info = "Windows tap device";
191
192         logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
193
194         return true;
195 }
196
197 static void enable_device(void) {
198         logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
199         ULONG status = 1;
200         DWORD len;
201         DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
202 }
203
204 static void disable_device(void) {
205         logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
206         ULONG status = 0;
207         DWORD len;
208         DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
209 }
210
211 static void close_device(void) {
212         io_del(&device_read_io);
213         CancelIo(device_handle);
214         CloseHandle(device_read_overlapped.hEvent);
215         CloseHandle(device_handle); device_handle = INVALID_HANDLE_VALUE;
216
217         free(device); device = NULL;
218         free(iface); iface = NULL;
219         device_info = NULL;
220 }
221
222 static bool read_packet(vpn_packet_t *packet) {
223         return false;
224 }
225
226 static bool write_packet(vpn_packet_t *packet) {
227         DWORD outlen;
228         OVERLAPPED overlapped = {0};
229
230         logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
231                            packet->len, device_info);
232
233         if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
234                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
235                 return false;
236         }
237
238         device_total_out += packet->len;
239
240         return true;
241 }
242
243 const devops_t os_devops = {
244         .setup = setup_device,
245         .close = close_device,
246         .read = read_packet,
247         .write = write_packet,
248         .enable = enable_device,
249         .disable = disable_device,
250 };