/* * tapctl -- Utility to manipulate TUN/TAP interfaces on Windows * https://community.openvpn.net/openvpn/wiki/Tapctl * * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> * Copyright (C) 2008-2013 David Sommerseth <dazo@users.sourceforge.net> * Copyright (C) 2018 Simon Rozman <simon@rozman.si> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include <config.h> #elif defined(_MSC_VER) #include <config-msvc.h> #endif #ifdef HAVE_CONFIG_VERSION_H #include <config-version.h> #endif #include "tap.h" #include "error.h" #include <objbase.h> #include <setupapi.h> #include <stdio.h> #include <tchar.h> #ifdef _MSC_VER #pragma comment(lib, "ole32.lib") #pragma comment(lib, "setupapi.lib") #endif const TCHAR title_string[] = TEXT(PACKAGE_NAME) TEXT(" ") TEXT(PACKAGE_VERSION) TEXT(" built on ") TEXT(__DATE__) ; static const TCHAR usage_message[] = TEXT("%s\n") TEXT("\n") TEXT("Usage:\n") TEXT("\n") TEXT("tapctl <command> [<command specific options>]\n") TEXT("\n") TEXT("Commands:\n") TEXT("\n") TEXT("create Create a new TUN/TAP interface\n") TEXT("list List network interfaces\n") TEXT("delete Delete specified network interface\n") TEXT("help Display this text\n") TEXT("\n") TEXT("Hint: Use \"tapctl help <command>\" to display help for particular command.\n") ; static const TCHAR usage_message_create[] = TEXT("%s\n") TEXT("\n") TEXT("Creates a new TUN/TAP interface\n") TEXT("\n") TEXT("Usage:\n") TEXT("\n") TEXT("tapctl create [<options>]\n") TEXT("\n") TEXT("Options:\n") TEXT("\n") TEXT("--name <name> Set TUN/TAP interface name. Should the interface with given name\n") TEXT(" already exist, an error is returned. If this option is not \n") TEXT(" specified, a default interface name is chosen by Windows. \n") TEXT(" Note: This name can also be specified as OpenVPN's --dev-node \n") TEXT(" option. \n") TEXT("\n") TEXT("Output:\n") TEXT("\n") TEXT("This command prints newly created TUN/TAP interface's GUID to stdout. \n") ; static const TCHAR usage_message_list[] = TEXT("%s\n") TEXT("\n") TEXT("Lists network interfaces\n") TEXT("\n") TEXT("Usage:\n") TEXT("\n") TEXT("tapctl list\n") TEXT("\n") TEXT("Output:\n") TEXT("\n") TEXT("This command prints all network interfaces to stdout. \n") ; static const TCHAR usage_message_delete[] = TEXT("%s\n") TEXT("\n") TEXT("Deletes the specified network interface\n") TEXT("\n") TEXT("Usage:\n") TEXT("\n") TEXT("tapctl delete <interface GUID | interface name>\n") ; /** * Print the help message. */ static void usage(void) { _ftprintf(stderr, usage_message, title_string); } /** * Program entry point */ int __cdecl _tmain(int argc, LPCTSTR argv[]) { int iResult; BOOL bRebootRequired = FALSE; /* Ask SetupAPI to keep quiet. */ SetupSetNonInteractiveMode(TRUE); if (argc < 2) { usage(); return 1; } else if (_tcsicmp(argv[1], TEXT("help")) == 0) { /* Output help. */ if (argc < 3) { usage(); } else if (_tcsicmp(argv[2], TEXT("create")) == 0) { _ftprintf(stderr, usage_message_create, title_string); } else if (_tcsicmp(argv[2], TEXT("list")) == 0) { _ftprintf(stderr, usage_message_list, title_string); } else if (_tcsicmp(argv[2], TEXT("delete")) == 0) { _ftprintf(stderr, usage_message_delete, title_string); } else { _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[2]); } return 1; } else if (_tcsicmp(argv[1], TEXT("create")) == 0) { LPCTSTR szName = NULL; /* Parse options. */ for (int i = 2; i < argc; i++) { if (_tcsicmp(argv[i], TEXT("--name")) == 0) { szName = argv[++i]; } else { _ftprintf(stderr, TEXT("Unknown option \"%s\". Please, use \"tapctl help create\" to list supported options. Ignored.\n"), argv[i]); } } /* Create TUN/TAP interface. */ GUID guidInterface; LPOLESTR szInterfaceId = NULL; DWORD dwResult = tap_create_interface( NULL, TEXT("Virtual Ethernet"), &bRebootRequired, &guidInterface); if (dwResult != ERROR_SUCCESS) { _ftprintf(stderr, TEXT("Creating TUN/TAP interface failed (error 0x%x).\n"), dwResult); iResult = 1; goto quit; } if (szName) { /* Get the list of available interfaces. */ struct tap_interface_node *pInterfaceList = NULL; dwResult = tap_list_interfaces(NULL, &pInterfaceList); if (dwResult != ERROR_SUCCESS) { _ftprintf(stderr, TEXT("Enumerating interfaces failed (error 0x%x).\n"), dwResult); iResult = 1; goto create_delete_interface; } /* Check for duplicates. */ for (struct tap_interface_node *pInterface = pInterfaceList; pInterface; pInterface = pInterface->pNext) { if (_tcsicmp(szName, pInterface->szName) == 0) { StringFromIID((REFIID)&pInterface->guid, &szInterfaceId); _ftprintf(stderr, TEXT("Interface \"%s\" already exists (GUID %") TEXT(PRIsLPOLESTR) TEXT(").\n"), pInterface->szName, szInterfaceId); CoTaskMemFree(szInterfaceId); iResult = 1; goto create_cleanup_pInterfaceList; } } /* Rename the interface. */ dwResult = tap_set_interface_name(&guidInterface, szName); if (dwResult != ERROR_SUCCESS) { StringFromIID((REFIID)&guidInterface, &szInterfaceId); _ftprintf(stderr, TEXT("Renaming TUN/TAP interface %") TEXT(PRIsLPOLESTR) TEXT(" to \"%s\" failed (error 0x%x).\n"), szInterfaceId, szName, dwResult); CoTaskMemFree(szInterfaceId); iResult = 1; goto quit; } iResult = 0; create_cleanup_pInterfaceList: tap_free_interface_list(pInterfaceList); if (iResult) { goto create_delete_interface; } } /* Output interface GUID. */ StringFromIID((REFIID)&guidInterface, &szInterfaceId); _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\n"), szInterfaceId); CoTaskMemFree(szInterfaceId); iResult = 0; goto quit; create_delete_interface: tap_delete_interface( NULL, &guidInterface, &bRebootRequired); iResult = 1; goto quit; } else if (_tcsicmp(argv[1], TEXT("list")) == 0) { /* Output list of network interfaces. */ struct tap_interface_node *pInterfaceList = NULL; DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList); if (dwResult != ERROR_SUCCESS) { _ftprintf(stderr, TEXT("Enumerating interfaces failed (error 0x%x).\n"), dwResult); iResult = 1; goto quit; } for (struct tap_interface_node *pInterface = pInterfaceList; pInterface; pInterface = pInterface->pNext) { LPOLESTR szInterfaceId = NULL; StringFromIID((REFIID)&pInterface->guid, &szInterfaceId); _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\t%") TEXT(PRIsLPTSTR) TEXT("\n"), szInterfaceId, pInterface->szName); CoTaskMemFree(szInterfaceId); } iResult = 0; tap_free_interface_list(pInterfaceList); } else if (_tcsicmp(argv[1], TEXT("delete")) == 0) { if (argc < 3) { _ftprintf(stderr, TEXT("Missing interface GUID or name. Please, use \"tapctl help delete\" for usage info.\n")); return 1; } GUID guidInterface; if (FAILED(IIDFromString(argv[2], (LPIID)&guidInterface))) { /* The argument failed to covert to GUID. Treat it as the interface name. */ struct tap_interface_node *pInterfaceList = NULL; DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList); if (dwResult != ERROR_SUCCESS) { _ftprintf(stderr, TEXT("Enumerating interfaces failed (error 0x%x).\n"), dwResult); iResult = 1; goto quit; } for (struct tap_interface_node *pInterface = pInterfaceList;; pInterface = pInterface->pNext) { if (pInterface == NULL) { _ftprintf(stderr, TEXT("\"%s\" interface not found.\n"), argv[2]); iResult = 1; goto delete_cleanup_pInterfaceList; } else if (_tcsicmp(argv[2], pInterface->szName) == 0) { memcpy(&guidInterface, &pInterface->guid, sizeof(GUID)); break; } } iResult = 0; delete_cleanup_pInterfaceList: tap_free_interface_list(pInterfaceList); if (iResult) { goto quit; } } /* Delete the network interface. */ DWORD dwResult = tap_delete_interface( NULL, &guidInterface, &bRebootRequired); if (dwResult != ERROR_SUCCESS) { _ftprintf(stderr, TEXT("Deleting interface \"%s\" failed (error 0x%x).\n"), argv[2], dwResult); iResult = 1; goto quit; } iResult = 0; goto quit; } else { _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[1]); return 1; } quit: if (bRebootRequired) { _ftprintf(stderr, TEXT("A system reboot is required.\n")); } return iResult; } bool dont_mute(unsigned int flags) { UNREFERENCED_PARAMETER(flags); return true; } void x_msg_va(const unsigned int flags, const char *format, va_list arglist) { /* Output message string. Note: Message strings don't contain line terminators. */ vfprintf(stderr, format, arglist); _ftprintf(stderr, TEXT("\n")); if ((flags & M_ERRNO) != 0) { /* Output system error message (if possible). */ DWORD dwResult = GetLastError(); LPTSTR szErrMessage = NULL; if (FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, 0, dwResult, 0, (LPTSTR)&szErrMessage, 0, NULL) && szErrMessage) { /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */ for (size_t i = 0, i_last = 0;; i++) { if (szErrMessage[i]) { if (!_istspace(szErrMessage[i])) { i_last = i + 1; } } else { szErrMessage[i_last] = 0; break; } } /* Output error message. */ _ftprintf(stderr, TEXT("Error 0x%x: %s\n"), dwResult, szErrMessage); LocalFree(szErrMessage); } else { _ftprintf(stderr, TEXT("Error 0x%x\n"), dwResult); } } }