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