OpenVPN
main.c
Go to the documentation of this file.
1/*
2 * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
3 * https://community.openvpn.net/openvpn/wiki/Tapctl
4 *
5 * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
6 * Copyright (C) 2018-2024 Simon Rozman <simon@rozman.si>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include "tap.h"
27#include "error.h"
28
29#include <objbase.h>
30#include <setupapi.h>
31#include <stdio.h>
32#include <wchar.h>
33
34#ifdef _MSC_VER
35#pragma comment(lib, "ole32.lib")
36#pragma comment(lib, "setupapi.lib")
37#endif
38
39
40const WCHAR title_string[] =
42;
43
44static const WCHAR usage_message[] =
45 L"%ls\n"
46 L"\n"
47 L"Usage:\n"
48 L"\n"
49 L"tapctl <command> [<command specific options>]\n"
50 L"\n"
51 L"Commands:\n"
52 L"\n"
53 L"create Create a new TUN/TAP adapter\n"
54 L"list List TUN/TAP adapters\n"
55 L"delete Delete specified network adapter\n"
56 L"help Display this text\n"
57 L"\n"
58 L"Hint: Use \"tapctl help <command>\" to display help for particular command.\n"
59;
60
61static const WCHAR usage_message_create[] =
62 L"%ls\n"
63 L"\n"
64 L"Creates a new TUN/TAP adapter\n"
65 L"\n"
66 L"Usage:\n"
67 L"\n"
68 L"tapctl create [<options>]\n"
69 L"\n"
70 L"Options:\n"
71 L"\n"
72 L"--name <name> Set TUN/TAP adapter name. Should the adapter with given name \n"
73 L" already exist, an error is returned. If this option is not \n"
74 L" specified, a default adapter name is chosen by Windows. \n"
75 L" Note: This name can also be specified as OpenVPN's --dev-node \n"
76 L" option. \n"
77 L"--hwid <hwid> Adapter hardware ID. Default value is root\\tap0901, which \n"
78 L" describes tap-windows6 driver. To work with ovpn-dco driver, \n"
79 L" driver, specify 'ovpn-dco'. \n"
80 L"\n"
81 L"Output:\n"
82 L"\n"
83 L"This command prints newly created TUN/TAP adapter's GUID to stdout. \n"
84;
85
86static const WCHAR usage_message_list[] =
87 L"%ls\n"
88 L"\n"
89 L"Lists TUN/TAP adapters\n"
90 L"\n"
91 L"Usage:\n"
92 L"\n"
93 L"tapctl list\n"
94 L"\n"
95 L"Options:\n"
96 L"\n"
97 L"--hwid <hwid> Adapter hardware ID. By default, root\\tap0901, tap0901 and \n"
98 L" ovpn-dco adapters are listed. Use this switch to limit the list.\n"
99 L"\n"
100 L"Output:\n"
101 L"\n"
102 L"This command prints all TUN/TAP adapters to stdout. \n"
103;
104
105static const WCHAR usage_message_delete[] =
106 L"%ls\n"
107 L"\n"
108 L"Deletes the specified network adapter\n"
109 L"\n"
110 L"Usage:\n"
111 L"\n"
112 L"tapctl delete <adapter GUID | adapter name>\n"
113;
114
115
119static void
120usage(void)
121{
122 fwprintf(stderr,
125}
126
130static BOOL
131is_adapter_name_available(LPCWSTR name, struct tap_adapter_node *adapter_list, BOOL log)
132{
133 for (struct tap_adapter_node *a = adapter_list; a; a = a->pNext)
134 {
135 if (wcsicmp(name, a->szName) == 0)
136 {
137 if (log)
138 {
139 LPOLESTR adapter_id = NULL;
140 StringFromIID((REFIID)&a->guid, &adapter_id);
141 fwprintf(stderr, L"Adapter \"%ls\" already exists (GUID %"
142 L"ls).\n", a->szName, adapter_id);
143 CoTaskMemFree(adapter_id);
144 }
145
146 return FALSE;
147 }
148 }
149
150 return TRUE;
151}
152
157static LPWSTR
158get_unique_adapter_name(LPCWSTR hwid, struct tap_adapter_node *adapter_list)
159{
160 if (hwid == NULL)
161 {
162 return NULL;
163 }
164
165 LPCWSTR base_name;
166 if (wcsicmp(hwid, L"ovpn-dco") == 0)
167 {
168 base_name = L"OpenVPN Data Channel Offload";
169 }
170 else if (wcsicmp(hwid, L"root\\" _L(TAP_WIN_COMPONENT_ID)) == 0)
171 {
172 base_name = L"OpenVPN TAP-Windows6";
173 }
174 else
175 {
176 return NULL;
177 }
178
179 if (is_adapter_name_available(base_name, adapter_list, FALSE))
180 {
181 return wcsdup(base_name);
182 }
183
184 size_t name_len = wcslen(base_name) + 10;
185 LPWSTR name = malloc(name_len * sizeof(WCHAR));
186 if (name == NULL)
187 {
188 return NULL;
189 }
190 for (int i = 1; i < 100; ++i)
191 {
192 swprintf_s(name, name_len, L"%ls #%d", base_name, i);
193
194 if (is_adapter_name_available(name, adapter_list, FALSE))
195 {
196 return name;
197 }
198 }
199
200 return NULL;
201}
202
206int __cdecl
207wmain(int argc, LPCWSTR argv[])
208{
209 int iResult;
210 BOOL bRebootRequired = FALSE;
211
212 /* Ask SetupAPI to keep quiet. */
213 SetupSetNonInteractiveMode(TRUE);
214
215 if (argc < 2)
216 {
217 usage();
218 return 1;
219 }
220 else if (wcsicmp(argv[1], L"help") == 0)
221 {
222 /* Output help. */
223 if (argc < 3)
224 {
225 usage();
226 }
227 else if (wcsicmp(argv[2], L"create") == 0)
228 {
229 fwprintf(stderr, usage_message_create, title_string);
230 }
231 else if (wcsicmp(argv[2], L"list") == 0)
232 {
233 fwprintf(stderr, usage_message_list, title_string);
234 }
235 else if (wcsicmp(argv[2], L"delete") == 0)
236 {
237 fwprintf(stderr, usage_message_delete, title_string);
238 }
239 else
240 {
241 fwprintf(stderr, L"Unknown command \"%ls"
242 L"\". Please, use \"tapctl help\" to list supported commands.\n", argv[2]);
243 }
244
245 return 1;
246 }
247 else if (wcsicmp(argv[1], L"create") == 0)
248 {
249 LPCWSTR szName = NULL;
250 LPCWSTR szHwId = L"root\\" _L(TAP_WIN_COMPONENT_ID);
251
252 /* Parse options. */
253 for (int i = 2; i < argc; i++)
254 {
255 if (wcsicmp(argv[i], L"--name") == 0)
256 {
257 szName = argv[++i];
258 }
259 else if (wcsicmp(argv[i], L"--hwid") == 0)
260 {
261 szHwId = argv[++i];
262 }
263 else
264 {
265 fwprintf(stderr, L"Unknown option \"%ls"
266 L"\". Please, use \"tapctl help create\" to list supported options. Ignored.\n",
267 argv[i]);
268 }
269 }
270
271 /* Create TUN/TAP adapter. */
272 GUID guidAdapter;
273 LPOLESTR szAdapterId = NULL;
274 DWORD dwResult = tap_create_adapter(
275 NULL,
276 L"Virtual Ethernet",
277 szHwId,
278 &bRebootRequired,
279 &guidAdapter);
280 if (dwResult != ERROR_SUCCESS)
281 {
282 fwprintf(stderr, L"Creating TUN/TAP adapter failed (error 0x%x).\n", dwResult);
283 iResult = 1; goto quit;
284 }
285
286 /* Get existing network adapters. */
287 struct tap_adapter_node *pAdapterList = NULL;
288 dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
289 if (dwResult != ERROR_SUCCESS)
290 {
291 fwprintf(stderr, L"Enumerating adapters failed (error 0x%x).\n", dwResult);
292 iResult = 1;
293 goto create_delete_adapter;
294 }
295
296 LPWSTR adapter_name = szName ? wcsdup(szName) : get_unique_adapter_name(szHwId, pAdapterList);
297 if (adapter_name)
298 {
299 /* Check for duplicates when name was specified,
300 * otherwise get_adapter_default_name() takes care of it */
301 if (szName && !is_adapter_name_available(adapter_name, pAdapterList, TRUE))
302 {
303 iResult = 1;
304 goto create_cleanup_pAdapterList;
305 }
306
307 /* Rename the adapter. */
308 dwResult = tap_set_adapter_name(&guidAdapter, adapter_name, FALSE);
309 if (dwResult != ERROR_SUCCESS)
310 {
311 StringFromIID((REFIID)&guidAdapter, &szAdapterId);
312 fwprintf(stderr, L"Renaming TUN/TAP adapter %ls"
313 L" to \"%ls\" failed (error 0x%x).\n",
314 szAdapterId, adapter_name, dwResult);
315 CoTaskMemFree(szAdapterId);
316 iResult = 1; goto quit;
317 }
318 }
319
320 iResult = 0;
321
322create_cleanup_pAdapterList:
323 free(adapter_name);
324
325 tap_free_adapter_list(pAdapterList);
326 if (iResult)
327 {
328 goto create_delete_adapter;
329 }
330
331 /* Output adapter GUID. */
332 StringFromIID((REFIID)&guidAdapter, &szAdapterId);
333 fwprintf(stdout, L"%ls\n", szAdapterId);
334 CoTaskMemFree(szAdapterId);
335
336 iResult = 0; goto quit;
337
338create_delete_adapter:
340 NULL,
341 &guidAdapter,
342 &bRebootRequired);
343 iResult = 1; goto quit;
344 }
345 else if (wcsicmp(argv[1], L"list") == 0)
346 {
347 WCHAR szzHwId[0x100] =
348 L"root\\" _L(TAP_WIN_COMPONENT_ID) L"\0"
350 L"ovpn-dco\0";
351
352 /* Parse options. */
353 for (int i = 2; i < argc; i++)
354 {
355 if (wcsicmp(argv[i], L"--hwid") == 0)
356 {
357 memset(szzHwId, 0, sizeof(szzHwId));
358 ++i;
359 memcpy_s(szzHwId, sizeof(szzHwId) - 2*sizeof(WCHAR) /*requires double zero termination*/, argv[i], wcslen(argv[i])*sizeof(WCHAR));
360 }
361 else
362 {
363 fwprintf(stderr, L"Unknown option \"%ls"
364 L"\". Please, use \"tapctl help list\" to list supported options. Ignored.\n",
365 argv[i]);
366 }
367 }
368
369 /* Output list of adapters with given hardware ID. */
370 struct tap_adapter_node *pAdapterList = NULL;
371 DWORD dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
372 if (dwResult != ERROR_SUCCESS)
373 {
374 fwprintf(stderr, L"Enumerating TUN/TAP adapters failed (error 0x%x).\n", dwResult);
375 iResult = 1; goto quit;
376 }
377
378 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
379 {
380 LPOLESTR szAdapterId = NULL;
381 StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
382 fwprintf(stdout, L"%ls\t%"
383 L"ls\n", szAdapterId, pAdapter->szName);
384 CoTaskMemFree(szAdapterId);
385 }
386
387 iResult = 0;
388 tap_free_adapter_list(pAdapterList);
389 }
390 else if (wcsicmp(argv[1], L"delete") == 0)
391 {
392 if (argc < 3)
393 {
394 fwprintf(stderr, L"Missing adapter GUID or name. Please, use \"tapctl help delete\" for usage info.\n");
395 return 1;
396 }
397
398 GUID guidAdapter;
399 if (FAILED(IIDFromString(argv[2], (LPIID)&guidAdapter)))
400 {
401 /* The argument failed to covert to GUID. Treat it as the adapter name. */
402 struct tap_adapter_node *pAdapterList = NULL;
403 DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
404 if (dwResult != ERROR_SUCCESS)
405 {
406 fwprintf(stderr, L"Enumerating TUN/TAP adapters failed (error 0x%x).\n", dwResult);
407 iResult = 1; goto quit;
408 }
409
410 for (struct tap_adapter_node *pAdapter = pAdapterList;; pAdapter = pAdapter->pNext)
411 {
412 if (pAdapter == NULL)
413 {
414 fwprintf(stderr, L"\"%ls\" adapter not found.\n", argv[2]);
415 iResult = 1; goto delete_cleanup_pAdapterList;
416 }
417 else if (wcsicmp(argv[2], pAdapter->szName) == 0)
418 {
419 memcpy(&guidAdapter, &pAdapter->guid, sizeof(GUID));
420 break;
421 }
422 }
423
424 iResult = 0;
425
426delete_cleanup_pAdapterList:
427 tap_free_adapter_list(pAdapterList);
428 if (iResult)
429 {
430 goto quit;
431 }
432 }
433
434 /* Delete the network adapter. */
435 DWORD dwResult = tap_delete_adapter(
436 NULL,
437 &guidAdapter,
438 &bRebootRequired);
439 if (dwResult != ERROR_SUCCESS)
440 {
441 fwprintf(stderr, L"Deleting adapter \"%ls"
442 L"\" failed (error 0x%x).\n", argv[2], dwResult);
443 iResult = 1; goto quit;
444 }
445
446 iResult = 0; goto quit;
447 }
448 else
449 {
450 fwprintf(stderr, L"Unknown command \"%ls"
451 L"\". Please, use \"tapctl help\" to list supported commands.\n", argv[1]);
452 return 1;
453 }
454
455quit:
456 if (bRebootRequired)
457 {
458 fwprintf(stderr, L"A system reboot is required.\n");
459 }
460
461 return iResult;
462}
463
464
465bool
466dont_mute(unsigned int flags)
467{
468 UNREFERENCED_PARAMETER(flags);
469
470 return true;
471}
472
473
474void
475x_msg_va(const unsigned int flags, const char *format, va_list arglist)
476{
477 /* Output message string. Note: Message strings don't contain line terminators. */
478 vfprintf(stderr, format, arglist);
479 fwprintf(stderr, L"\n");
480
481 if ((flags & M_ERRNO) != 0)
482 {
483 /* Output system error message (if possible). */
484 DWORD dwResult = GetLastError();
485 LPWSTR szErrMessage = NULL;
486 if (FormatMessage(
487 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
488 0,
489 dwResult,
490 0,
491 (LPWSTR)&szErrMessage,
492 0,
493 NULL) && szErrMessage)
494 {
495 /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
496 for (size_t i = 0, i_last = 0;; i++)
497 {
498 if (szErrMessage[i])
499 {
500 if (!iswspace(szErrMessage[i]))
501 {
502 i_last = i + 1;
503 }
504 }
505 else
506 {
507 szErrMessage[i_last] = 0;
508 break;
509 }
510 }
511
512 /* Output error message. */
513 fwprintf(stderr, L"Error 0x%x: %ls\n", dwResult, szErrMessage);
514
515 LocalFree(szErrMessage);
516 }
517 else
518 {
519 fwprintf(stderr, L"Error 0x%x\n", dwResult);
520 }
521 }
522}
#define PACKAGE_NAME
Definition config.h:492
#define TAP_WIN_COMPONENT_ID
Definition config.h:546
#define PACKAGE_VERSION
Definition config.h:504
int __cdecl wmain(int argc, LPCWSTR argv[])
Program entry point.
Definition main.c:207
static const WCHAR usage_message_delete[]
Definition main.c:105
static BOOL is_adapter_name_available(LPCWSTR name, struct tap_adapter_node *adapter_list, BOOL log)
Checks if adapter with given name doesn't already exist.
Definition main.c:131
static const WCHAR usage_message_list[]
Definition main.c:86
bool dont_mute(unsigned int flags)
Check muting filter.
Definition main.c:466
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition main.c:475
static LPWSTR get_unique_adapter_name(LPCWSTR hwid, struct tap_adapter_node *adapter_list)
Returns unique adapter name based on hwid or NULL if name cannot be generated.
Definition main.c:158
const WCHAR title_string[]
Definition main.c:40
static const WCHAR usage_message_create[]
Definition main.c:61
static const WCHAR usage_message[]
Definition main.c:44
static void usage(void)
Print the help message.
Definition main.c:120
#define M_ERRNO
Definition error.h:94
Definition argv.h:35
Network adapter list node.
Definition tap.h:137
struct tap_adapter_node * pNext
Pointer to next adapter.
Definition tap.h:142
LPWSTR szName
Adapter name.
Definition tap.h:140
DWORD tap_list_adapters(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szzHwIDs, _Out_ struct tap_adapter_node **ppAdapter)
Creates a list of existing network adapters.
Definition tap.c:1146
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCWSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition tap.c:1063
DWORD tap_create_adapter(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szDeviceDescription, _In_ LPCWSTR szHwId, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidAdapter)
Creates a TUN/TAP adapter.
Definition tap.c:723
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition tap.c:1005
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition tap.c:1355
#define _L(q)
Definition basic.h:37