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