1 /*
2 device.c -- Interaction with Windows tap driver in a Cygwin environment
3 Copyright (C) 2002-2004 Ivo Timmermans <ivo@tinc-vpn.org>,
4 2002-2004 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
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "cpu_emulation.h"
22 #include "main.h"
23 #include "ethernet_cygwin.h"
24
25 #define DEBUG 0
26 #include "debug.h"
27
28 #include <winioctl.h>
29
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33
34 #include <errno.h>
35
36 #define MTU 1500
37
38 //=============
39 // TAP IOCTLs
40 //=============
41
42 #define TAP_CONTROL_CODE(request,method) \
43 CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
44
45 #define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
46 #define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
47 #define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
48 #define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
49 #define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
50 #define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
51 #define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
52 #define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
53 #define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
54
55 //=================
56 // Registry keys
57 //=================
58
59 #define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
60
61 #define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
62
63 //======================
64 // Filesystem prefixes
65 //======================
66
67 #define USERMODEDEVICEDIR "\\\\.\\Global\\"
68 #define SYSDEVICEDIR "\\Device\\"
69 #define USERDEVICEDIR "\\DosDevices\\Global\\"
70 #define TAPSUFFIX ".tap"
71
72 //=========================================================
73 // TAP_COMPONENT_ID -- This string defines the TAP driver
74 // type -- different component IDs can reside in the system
75 // simultaneously.
76 //=========================================================
77
78 #define TAP_COMPONENT_ID "tap0801"
79
winerror(int err)80 static char *winerror(int err) {
81 static char buf[1024], *newline;
82
83 if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
84 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
85 strncpy(buf, "(unable to format errormessage)", sizeof(buf));
86 }
87
88 if((newline = strchr(buf, '\r')))
89 *newline = '\0';
90
91 return buf;
92 }
93
94
WinTapEthernetHandler(int eth_idx)95 WinTapEthernetHandler::WinTapEthernetHandler(int eth_idx)
96 : ETHERNETDriver::Handler(eth_idx)
97 {
98 device_handle = INVALID_HANDLE_VALUE;
99 device = NULL;
100 iface = NULL;
101
102 device_total_in = 0;
103 device_total_out = 0;
104 }
105
~WinTapEthernetHandler()106 WinTapEthernetHandler::~WinTapEthernetHandler()
107 {
108 close();
109 }
110
is_tap_win32_dev(const char * guid)111 bool is_tap_win32_dev(const char *guid)
112 {
113 HKEY netcard_key;
114 LONG status;
115 DWORD len;
116 int i = 0;
117
118 status = RegOpenKeyEx(
119 HKEY_LOCAL_MACHINE,
120 ADAPTER_KEY,
121 0,
122 KEY_READ,
123 &netcard_key);
124
125 if (status != ERROR_SUCCESS) {
126 D(bug("WinTap: Error opening registry key: %s", ADAPTER_KEY));
127 return false;
128 }
129
130 while (true)
131 {
132 char enum_name[256];
133 char unit_string[256];
134 HKEY unit_key;
135 char component_id_string[] = "ComponentId";
136 char component_id[256];
137 char net_cfg_instance_id_string[] = "NetCfgInstanceId";
138 char net_cfg_instance_id[256];
139 DWORD data_type;
140
141 len = sizeof (enum_name);
142 status = RegEnumKeyEx(
143 netcard_key,
144 i,
145 enum_name,
146 &len,
147 NULL,
148 NULL,
149 NULL,
150 NULL);
151
152 if (status == ERROR_NO_MORE_ITEMS)
153 break;
154 else if (status != ERROR_SUCCESS) {
155 D(bug("WinTap: Error enumerating registry subkeys of key: %s",
156 ADAPTER_KEY));
157 return false;
158 }
159
160 snprintf (unit_string, sizeof(unit_string), "%s\\%s",
161 ADAPTER_KEY, enum_name);
162
163 status = RegOpenKeyEx(
164 HKEY_LOCAL_MACHINE,
165 unit_string,
166 0,
167 KEY_READ,
168 &unit_key);
169
170 if (status != ERROR_SUCCESS) {
171 D(bug("WinTap: Error opening registry key: %s", unit_string));
172 return false;
173 }
174 else
175 {
176 len = sizeof (component_id);
177 status = RegQueryValueEx(
178 unit_key,
179 component_id_string,
180 NULL,
181 &data_type,
182 (BYTE*)component_id,
183 &len);
184
185 if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
186 len = sizeof (net_cfg_instance_id);
187 status = RegQueryValueEx(
188 unit_key,
189 net_cfg_instance_id_string,
190 NULL,
191 &data_type,
192 (BYTE*)net_cfg_instance_id,
193 &len);
194
195 if (status == ERROR_SUCCESS && data_type == REG_SZ)
196 {
197 if (!strcmp (net_cfg_instance_id, guid))
198 {
199 RegCloseKey (unit_key);
200 RegCloseKey (netcard_key);
201 return true;
202 }
203 }
204 }
205 RegCloseKey (unit_key);
206 }
207 ++i;
208 }
209
210 RegCloseKey (netcard_key);
211 return false;
212 }
213
214
get_device_guid(char * name,int name_size,char * actual_name,int actual_name_size)215 int get_device_guid(
216 char *name,
217 int name_size,
218 char *actual_name,
219 int actual_name_size)
220 {
221 LONG status;
222 HKEY control_net_key;
223 DWORD len;
224 int i = 0;
225 int stop = 0;
226
227 status = RegOpenKeyEx(
228 HKEY_LOCAL_MACHINE,
229 NETWORK_CONNECTIONS_KEY,
230 0,
231 KEY_READ,
232 &control_net_key);
233
234 if (status != ERROR_SUCCESS) {
235 D(bug("WinTap: Error opening registry key: %s", NETWORK_CONNECTIONS_KEY));
236 return -1;
237 }
238
239 while (!stop)
240 {
241 char enum_name[256];
242 char connection_string[256];
243 HKEY connection_key;
244 char name_data[256];
245 DWORD name_type;
246 const char name_string[] = "Name";
247
248 len = sizeof (enum_name);
249 status = RegEnumKeyEx(
250 control_net_key,
251 i,
252 enum_name,
253 &len,
254 NULL,
255 NULL,
256 NULL,
257 NULL);
258
259 if (status == ERROR_NO_MORE_ITEMS)
260 break;
261 else if (status != ERROR_SUCCESS) {
262 D(bug("WinTap: Error enumerating registry subkeys of key: %s",
263 NETWORK_CONNECTIONS_KEY));
264 return -1;
265 }
266
267 snprintf(connection_string,
268 sizeof(connection_string),
269 "%s\\%s\\Connection",
270 NETWORK_CONNECTIONS_KEY, enum_name);
271
272 status = RegOpenKeyEx(
273 HKEY_LOCAL_MACHINE,
274 connection_string,
275 0,
276 KEY_READ,
277 &connection_key);
278
279 if (status == ERROR_SUCCESS) {
280 len = sizeof (name_data);
281 status = RegQueryValueEx(
282 connection_key,
283 name_string,
284 NULL,
285 &name_type,
286 (BYTE*)name_data,
287 &len);
288
289 if (status != ERROR_SUCCESS || name_type != REG_SZ) {
290 D(bug("WinTap: Error opening registry key: %s\\%s\\%s",
291 NETWORK_CONNECTIONS_KEY, connection_string, name_string));
292 return -1;
293 }
294 else {
295 if (is_tap_win32_dev(enum_name)) {
296 D(bug("WinTap: found TAP device named \"%s\" ~ \"%s\"", name_data, actual_name));
297
298 snprintf(name, name_size, "%s", enum_name);
299 if (actual_name) {
300 if (strcmp(actual_name, "") != 0) {
301 if (strcmp(name_data, actual_name) != 0) {
302 RegCloseKey (connection_key);
303 ++i;
304 continue;
305 }
306 }
307 else {
308 snprintf(actual_name, actual_name_size, "%s", name_data);
309 }
310 }
311 stop = 1;
312 }
313 }
314
315 RegCloseKey (connection_key);
316 }
317 ++i;
318 }
319
320 RegCloseKey (control_net_key);
321
322 if (stop == 0)
323 return -1;
324
325 return 0;
326 }
327
open()328 bool WinTapEthernetHandler::open()
329 {
330 char *type = bx_options.ethernet[ethX].type;
331 char device_path[256];
332 char device_guid[0x100];
333 char name_buffer[0x100];
334
335 close();
336
337 if (strcmp(type, "none") == 0 || strlen(type) == 0)
338 {
339 return false;
340 }
341
342 if ( strlen(bx_options.ethernet[ethX].tunnel) == 0) {
343 D(bug("WinTap(%d): tunnel name undefined", ethX));
344 return false;
345 }
346
347 safe_strncpy(name_buffer, bx_options.ethernet[ethX].tunnel, sizeof(name_buffer));
348
349 if ( get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)) < 0 ) {
350 panicbug("WinTap: ERROR: Could not find Windows tap device: %s", winerror(GetLastError()));
351 return false;
352 }
353
354 /*
355 * Open Windows TAP-Win32 adapter
356 */
357 snprintf (device_path, sizeof(device_path), "%s%s%s",
358 USERMODEDEVICEDIR,
359 device_guid,
360 TAPSUFFIX);
361
362 device_handle = CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
363
364 if (device_handle == INVALID_HANDLE_VALUE) {
365 panicbug("WinTap: ERROR: Could not open (%s) Windows tap device: %s", device_path, winerror(GetLastError()));
366 return false;
367 }
368 device = strdup(device_path);
369
370 read_overlapped.Offset = 0;
371 read_overlapped.OffsetHigh = 0;
372 read_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
373 write_overlapped.Offset = 0;
374 write_overlapped.OffsetHigh = 0;
375 write_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
376
377 D(bug("WinTap: tap device open %s [handle=%p]", device_path, (void*)device_handle));
378 return true;
379 }
380
close()381 void WinTapEthernetHandler::close()
382 {
383 if (device_handle != INVALID_HANDLE_VALUE)
384 {
385 CloseHandle(device_handle);
386 device_handle = INVALID_HANDLE_VALUE;
387 }
388 free(device);
389 device = NULL;
390 }
391
recv(uint8 * buf,int len)392 int WinTapEthernetHandler::recv(uint8 *buf, int len)
393 {
394 DWORD lenin;
395
396 D(bug("WinTap: Read packet from %s", device));
397
398 BOOL result = ReadFile(device_handle, buf, len, &lenin, &read_overlapped);
399 if (!result) {
400 DWORD error = GetLastError();
401 switch (error)
402 {
403 case ERROR_IO_PENDING:
404 WaitForSingleObject(read_overlapped.hEvent, INFINITE);
405 result = GetOverlappedResult( device_handle, &read_overlapped, &lenin, 0 );
406 if ( result ) break;
407 /* fallthrough */
408 default:
409 D(bug("WinTap: Error while reading from %s: %s", device, winerror(GetLastError())));
410 return -1;
411 }
412 }
413
414 device_total_in += lenin;
415 D(bug("WinTap: Read packet done (len %d)", (int)lenin));
416 return lenin;
417 }
418
send(const uint8 * buf,int len)419 int WinTapEthernetHandler::send(const uint8 *buf, int len)
420 {
421 DWORD lenout;
422
423 D(bug("WinTap: Writing packet of %d bytes to %s", len, device));
424
425 BOOL result = WriteFile (device_handle, buf, len, &lenout, &write_overlapped);
426 if (!result) {
427 DWORD error = GetLastError();
428 switch (error)
429 {
430 case ERROR_IO_PENDING:
431 WaitForSingleObject(write_overlapped.hEvent, INFINITE);
432 break;
433 default:
434 D(bug("WinTap: Error while writing to %s: %s", device, winerror(GetLastError())));
435 return -1;
436 }
437 }
438
439 device_total_out += lenout;
440 D(bug("WinTap: Writing packet done"));
441 return lenout;
442 }
443