2013-07-04 20:56:19 +00:00
/*
2015-02-17 21:11:34 +00:00
* ZeroTier One - Network Virtualization Everywhere
* Copyright ( C ) 2011 - 2015 ZeroTier , Inc .
2013-07-04 20:56:19 +00:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*
* - -
*
* ZeroTier may be used and distributed under the terms of the GPLv3 , which
* are available at : http : //www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form , please contact ZeroTier Networks
* LLC . Start here : http : //www.zerotier.com/
*/
2014-04-08 19:00:21 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <string.h>
# include <WinSock2.h>
# include <Windows.h>
# include <tchar.h>
# include <winreg.h>
# include <wchar.h>
# include <ws2ipdef.h>
# include <WS2tcpip.h>
# include <IPHlpApi.h>
# include <nldef.h>
# include <netioapi.h>
2014-08-13 00:20:34 +00:00
# include <atlbase.h>
# include <netlistmgr.h>
# include <nldef.h>
# include <iostream>
2015-04-24 22:05:28 +00:00
# include <set>
2014-04-08 19:00:21 +00:00
2014-07-28 16:08:56 +00:00
# include "../node/Constants.hpp"
# include "../node/Utils.hpp"
# include "../node/Mutex.hpp"
2014-04-08 19:00:21 +00:00
2015-04-09 02:03:30 +00:00
# include "WindowsEthernetTap.hpp"
2015-04-24 22:05:28 +00:00
# include "OSUtils.hpp"
2015-04-09 02:03:30 +00:00
2015-05-08 23:31:50 +00:00
# include "..\windows\TapDriver6\tap-windows.h"
2013-07-06 19:56:12 +00:00
// ff:ff:ff:ff:ff:ff with no ADI
2015-04-24 22:05:28 +00:00
//static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
2013-07-04 20:56:19 +00:00
2014-08-13 00:20:34 +00:00
# define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
2013-07-04 20:56:19 +00:00
namespace ZeroTier {
2015-04-24 22:05:28 +00:00
namespace {
class WindowsEthernetTapEnv
{
public :
WindowsEthernetTapEnv ( )
{
# ifdef _WIN64
is64Bit = TRUE ;
devcon = " \\ devcon_x64.exe " ;
2015-04-25 22:22:43 +00:00
tapDriverNdis5 = " \\ tap-windows \\ x64 \\ zttap200.inf " ;
tapDriverNdis6 = " \\ tap-windows \\ x64 \\ zttap300.inf " ;
2015-04-24 22:05:28 +00:00
# else
is64Bit = FALSE ;
IsWow64Process ( GetCurrentProcess ( ) , & is64Bit ) ;
devcon = ( ( is64Bit = = TRUE ) ? " \\ devcon_x64.exe " : " \\ devcon_x86.exe " ) ;
2015-04-25 22:22:43 +00:00
tapDriverNdis5 = ( ( is64Bit = = TRUE ) ? " \\ tap-windows \\ x64 \\ zttap200.inf " : " \\ tap-windows \\ x86 \\ zttap200.inf " ) ;
tapDriverNdis6 = ( ( is64Bit = = TRUE ) ? " \\ tap-windows \\ x64 \\ zttap300.inf " : " \\ tap-windows \\ x86 \\ zttap300.inf " ) ;
2015-04-24 22:05:28 +00:00
# endif
}
BOOL is64Bit ;
2015-04-25 22:22:43 +00:00
const char * devcon ;
const char * tapDriverNdis5 ;
const char * tapDriverNdis6 ;
2015-04-24 22:05:28 +00:00
} ;
static const WindowsEthernetTapEnv WINENV ;
2014-04-08 23:10:48 +00:00
// Only create or delete devices one at a time
2014-04-08 19:00:21 +00:00
static Mutex _systemTapInitLock ;
2014-01-27 06:47:08 +00:00
2015-05-08 23:31:50 +00:00
} // anonymous namespace
2014-04-08 19:00:21 +00:00
WindowsEthernetTap : : WindowsEthernetTap (
2015-04-24 22:05:28 +00:00
const char * hp ,
2013-08-14 17:23:25 +00:00
const MAC & mac ,
unsigned int mtu ,
2014-08-05 04:48:59 +00:00
unsigned int metric ,
uint64_t nwid ,
const char * friendlyName ,
2015-04-24 22:05:28 +00:00
void ( * handler ) ( void * , uint64_t , const MAC & , const MAC & , unsigned int , unsigned int , const void * , unsigned int ) ,
2014-08-05 04:48:59 +00:00
void * arg ) :
2013-08-14 17:23:25 +00:00
_handler ( handler ) ,
2013-08-25 22:18:02 +00:00
_arg ( arg ) ,
2015-04-24 22:05:28 +00:00
_mac ( mac ) ,
2014-08-13 00:20:34 +00:00
_nwid ( nwid ) ,
2013-08-25 22:18:02 +00:00
_tap ( INVALID_HANDLE_VALUE ) ,
_injectSemaphore ( INVALID_HANDLE_VALUE ) ,
2015-04-24 22:05:28 +00:00
_pathToHelpers ( hp ) ,
2014-01-28 07:13:36 +00:00
_run ( true ) ,
2014-04-08 19:00:21 +00:00
_initialized ( false ) ,
2014-04-08 22:47:33 +00:00
_enabled ( true )
2013-08-14 17:23:25 +00:00
{
2013-08-23 21:39:21 +00:00
char subkeyName [ 4096 ] ;
char subkeyClass [ 4096 ] ;
char data [ 4096 ] ;
2014-08-05 04:48:59 +00:00
char tag [ 24 ] ;
2013-08-22 18:30:55 +00:00
2014-08-05 04:48:59 +00:00
if ( mtu > 2800 )
2013-08-26 21:22:20 +00:00
throw std : : runtime_error ( " MTU too large for Windows tap " ) ;
2014-04-08 23:10:48 +00:00
Mutex : : Lock _l ( _systemTapInitLock ) ;
2013-08-22 18:30:55 +00:00
2015-05-08 23:31:50 +00:00
// Use NDIS5 if it's installed, since we don't want to switch out the driver on
// pre-existing installs (yet). We won't ship NDIS5 anymore so new installs will
// use NDIS6.
2015-05-08 19:35:03 +00:00
std : : string tapDriverPath ( _pathToHelpers + WINENV . tapDriverNdis5 ) ;
const char * tapDriverName = " zttap200 " ;
2015-04-25 22:22:43 +00:00
if ( : : PathFileExistsA ( tapDriverPath . c_str ( ) ) = = FALSE ) {
2015-05-08 19:35:03 +00:00
tapDriverPath = _pathToHelpers + WINENV . tapDriverNdis6 ;
tapDriverName = " zttap300 " ;
2015-04-25 22:22:43 +00:00
if ( : : PathFileExistsA ( tapDriverPath . c_str ( ) ) = = FALSE ) {
throw std : : runtime_error ( " no tap driver available: cannot find zttap300.inf (NDIS6) or zttap200.inf (NDIS5) under home path " ) ;
}
}
2013-08-21 14:18:05 +00:00
HKEY nwAdapters ;
2013-08-22 18:30:55 +00:00
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Control \\ Class \\ {4D36E972-E325-11CE-BFC1-08002BE10318} " , 0 , KEY_READ | KEY_WRITE , & nwAdapters ) ! = ERROR_SUCCESS )
2013-08-21 14:18:05 +00:00
throw std : : runtime_error ( " unable to open registry key for network adapter enumeration " ) ;
2013-08-22 18:30:55 +00:00
std : : set < std : : string > existingDeviceInstances ;
2013-08-23 21:39:21 +00:00
std : : string mySubkeyName ;
2013-08-22 18:30:55 +00:00
2014-08-05 04:48:59 +00:00
// We "tag" registry entries with the network ID to identify persistent devices
Utils : : snprintf ( tag , sizeof ( tag ) , " %.16llx " , ( unsigned long long ) nwid ) ;
// Look for the tap instance that corresponds with this network
2014-03-06 22:06:31 +00:00
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
2013-08-22 18:30:55 +00:00
DWORD type ;
DWORD dataLen ;
2013-08-21 14:18:05 +00:00
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
2014-03-06 22:06:31 +00:00
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ! strnicmp ( data , " zttap " , 5 ) ) {
std : : string instanceId ;
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " NetCfgInstanceId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
instanceId . assign ( data , dataLen ) ;
existingDeviceInstances . insert ( instanceId ) ;
}
2013-08-22 18:30:55 +00:00
2014-03-06 22:06:31 +00:00
std : : string instanceIdPath ;
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
instanceIdPath . assign ( data , dataLen ) ;
2014-04-08 19:00:21 +00:00
if ( ( _netCfgInstanceId . length ( ) = = 0 ) & & ( instanceId . length ( ) ! = 0 ) & & ( instanceIdPath . length ( ) ! = 0 ) ) {
2013-08-26 21:48:47 +00:00
type = 0 ;
dataLen = sizeof ( data ) ;
2014-03-06 22:06:31 +00:00
if ( RegGetValueA ( nwAdapters , subkeyName , " _ZeroTierTapIdentifier " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ! strcmp ( data , tag ) ) {
2014-04-08 19:00:21 +00:00
_netCfgInstanceId = instanceId ;
_deviceInstanceId = instanceIdPath ;
2014-08-05 04:48:59 +00:00
2014-03-06 22:06:31 +00:00
mySubkeyName = subkeyName ;
break ; // found it!
2013-08-22 18:30:55 +00:00
}
}
}
2013-08-21 14:18:05 +00:00
}
2014-03-06 22:06:31 +00:00
}
} else break ; // no more subkeys or error occurred enumerating them
2013-08-22 18:30:55 +00:00
}
// If there is no device, try to create one
2014-08-13 00:20:34 +00:00
bool creatingNewDevice = ( _netCfgInstanceId . length ( ) = = 0 ) ;
if ( creatingNewDevice ) {
2014-03-06 22:06:31 +00:00
// Log devcon output to a file
2014-08-05 04:48:59 +00:00
HANDLE devconLog = CreateFileA ( ( _pathToHelpers + " \\ devcon.log " ) . c_str ( ) , GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( devconLog ! = INVALID_HANDLE_VALUE )
2014-03-06 22:06:31 +00:00
SetFilePointer ( devconLog , 0 , 0 , FILE_END ) ;
2015-05-08 23:31:50 +00:00
// Execute devcon to create a new tap device
2013-08-22 18:30:55 +00:00
STARTUPINFOA startupInfo ;
startupInfo . cb = sizeof ( startupInfo ) ;
2014-03-06 22:06:31 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE ) {
SetFilePointer ( devconLog , 0 , 0 , FILE_END ) ;
startupInfo . hStdOutput = devconLog ;
startupInfo . hStdError = devconLog ;
}
2013-08-22 18:30:55 +00:00
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
2015-04-25 22:22:43 +00:00
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WINENV . devcon + " \" install \" " + tapDriverPath + " \" " + tapDriverName ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
2013-08-22 18:30:55 +00:00
RegCloseKey ( nwAdapters ) ;
2014-03-06 22:06:31 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
2015-04-24 22:05:28 +00:00
throw std : : runtime_error ( std : : string ( " unable to find or execute devcon at " ) + WINENV . devcon ) ;
2013-08-22 18:30:55 +00:00
}
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
2014-03-06 22:06:31 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
2014-08-05 04:48:59 +00:00
// Scan for the new instance by simply looking for taps that weren't originally there...
2014-03-06 22:06:31 +00:00
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
2013-08-22 18:30:55 +00:00
DWORD type ;
DWORD dataLen ;
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
2014-03-06 22:06:31 +00:00
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ! strnicmp ( data , " zttap " , 5 ) ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " NetCfgInstanceId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
if ( existingDeviceInstances . count ( std : : string ( data , dataLen ) ) = = 0 ) {
RegSetKeyValueA ( nwAdapters , subkeyName , " _ZeroTierTapIdentifier " , REG_SZ , tag , ( DWORD ) ( strlen ( tag ) + 1 ) ) ;
2014-04-08 19:00:21 +00:00
_netCfgInstanceId . assign ( data , dataLen ) ;
2014-03-06 22:06:31 +00:00
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
2014-04-08 19:00:21 +00:00
_deviceInstanceId . assign ( data , dataLen ) ;
2014-03-06 22:06:31 +00:00
mySubkeyName = subkeyName ;
// Disable DHCP by default on newly created devices
HKEY tcpIpInterfaces ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ services \\ Tcpip \\ Parameters \\ Interfaces " , 0 , KEY_READ | KEY_WRITE , & tcpIpInterfaces ) = = ERROR_SUCCESS ) {
DWORD enable = 0 ;
2014-04-08 19:00:21 +00:00
RegSetKeyValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , " EnableDHCP " , REG_DWORD , & enable , sizeof ( enable ) ) ;
2014-03-06 22:06:31 +00:00
RegCloseKey ( tcpIpInterfaces ) ;
2013-08-22 18:30:55 +00:00
}
2014-03-06 22:06:31 +00:00
break ; // found it!
2013-08-22 18:30:55 +00:00
}
}
}
2014-03-06 22:06:31 +00:00
}
} else break ; // no more keys or error occurred
2013-08-21 14:18:05 +00:00
}
}
2014-04-08 19:00:21 +00:00
if ( _netCfgInstanceId . length ( ) > 0 ) {
2014-08-05 04:48:59 +00:00
char tmps [ 64 ] ;
2014-05-23 23:21:57 +00:00
unsigned int tmpsl = Utils : : snprintf ( tmps , sizeof ( tmps ) , " %.2X-%.2X-%.2X-%.2X-%.2X-%.2X " , ( unsigned int ) mac [ 0 ] , ( unsigned int ) mac [ 1 ] , ( unsigned int ) mac [ 2 ] , ( unsigned int ) mac [ 3 ] , ( unsigned int ) mac [ 4 ] , ( unsigned int ) mac [ 5 ] ) + 1 ;
2013-08-25 22:18:02 +00:00
RegSetKeyValueA ( nwAdapters , mySubkeyName . c_str ( ) , " NetworkAddress " , REG_SZ , tmps , tmpsl ) ;
RegSetKeyValueA ( nwAdapters , mySubkeyName . c_str ( ) , " MAC " , REG_SZ , tmps , tmpsl ) ;
2013-08-23 21:39:21 +00:00
DWORD tmp = mtu ;
RegSetKeyValueA ( nwAdapters , mySubkeyName . c_str ( ) , " MTU " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
2014-08-13 00:20:34 +00:00
2013-08-23 21:39:21 +00:00
tmp = 0 ;
2014-08-13 00:20:34 +00:00
RegSetKeyValueA ( nwAdapters , mySubkeyName . c_str ( ) , " *NdisDeviceType " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
tmp = IF_TYPE_ETHERNET_CSMACD ;
RegSetKeyValueA ( nwAdapters , mySubkeyName . c_str ( ) , " *IfType " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
if ( creatingNewDevice ) {
tmp = 0 ;
RegSetKeyValueA ( nwAdapters , mySubkeyName . c_str ( ) , " EnableDHCP " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
}
2014-04-08 23:10:48 +00:00
RegCloseKey ( nwAdapters ) ;
2014-03-06 22:06:31 +00:00
} else {
RegCloseKey ( nwAdapters ) ;
throw std : : runtime_error ( " unable to find or create tap adapter " ) ;
2013-08-23 21:39:21 +00:00
}
2013-08-26 21:22:20 +00:00
{
char nobraces [ 128 ] ;
2014-04-08 19:00:21 +00:00
const char * nbtmp1 = _netCfgInstanceId . c_str ( ) ;
2013-08-26 21:22:20 +00:00
char * nbtmp2 = nobraces ;
while ( * nbtmp1 ) {
if ( ( * nbtmp1 ! = ' { ' ) & & ( * nbtmp1 ! = ' } ' ) )
* nbtmp2 + + = * nbtmp1 ;
+ + nbtmp1 ;
}
* nbtmp2 = ( char ) 0 ;
if ( UuidFromStringA ( ( RPC_CSTR ) nobraces , & _deviceGuid ) ! = RPC_S_OK )
throw std : : runtime_error ( " unable to convert instance ID GUID to native GUID (invalid NetCfgInstanceId in registry?) " ) ;
}
2014-08-05 04:48:59 +00:00
// Look up interface LUID... why are there (at least) four fucking ways to refer to a network device in Windows?
if ( ConvertInterfaceGuidToLuid ( & _deviceGuid , & _deviceLuid ) ! = NO_ERROR )
throw std : : runtime_error ( " unable to convert device interface GUID to LUID " ) ;
2015-05-08 23:31:50 +00:00
// Certain functions can now work (e.g. ips())
_initialized = true ;
2014-08-05 04:48:59 +00:00
if ( friendlyName )
setFriendlyName ( friendlyName ) ;
2013-08-27 15:55:56 +00:00
// Start background thread that actually performs I/O
2013-08-25 22:18:02 +00:00
_injectSemaphore = CreateSemaphore ( NULL , 0 , 1 , NULL ) ;
_thread = Thread : : start ( this ) ;
2013-08-14 17:23:25 +00:00
}
2014-04-08 19:00:21 +00:00
WindowsEthernetTap : : ~ WindowsEthernetTap ( )
2013-08-14 17:23:25 +00:00
{
2013-08-25 22:18:02 +00:00
_run = false ;
ReleaseSemaphore ( _injectSemaphore , 1 , NULL ) ;
Thread : : join ( _thread ) ;
CloseHandle ( _injectSemaphore ) ;
2014-08-05 04:48:59 +00:00
_disableTapDevice ( ) ;
2014-04-08 19:00:21 +00:00
}
2014-01-21 21:07:22 +00:00
2014-04-08 19:00:21 +00:00
void WindowsEthernetTap : : setEnabled ( bool en )
{
2014-04-08 22:47:33 +00:00
_enabled = en ;
2013-08-14 17:23:25 +00:00
}
2014-04-08 19:00:21 +00:00
bool WindowsEthernetTap : : enabled ( ) const
{
return _enabled ;
}
2015-04-24 22:05:28 +00:00
bool WindowsEthernetTap : : addIp ( const InetAddress & ip )
2013-08-14 17:23:25 +00:00
{
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
return false ;
2014-01-23 22:15:00 +00:00
if ( ! ip . netmaskBits ( ) ) // sanity check... netmask of 0.0.0.0 is WUT?
2013-08-26 21:22:20 +00:00
return false ;
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > haveIps ( ips ( ) ) ;
2013-08-26 21:22:20 +00:00
2014-01-23 22:15:00 +00:00
try {
// Add IP to interface at the netlink level if not already assigned.
2015-04-24 22:05:28 +00:00
if ( ! std : : binary_search ( haveIps . begin ( ) , haveIps . end ( ) , ip ) ) {
2014-01-23 22:15:00 +00:00
MIB_UNICASTIPADDRESS_ROW ipr ;
InitializeUnicastIpAddressEntry ( & ipr ) ;
if ( ip . isV4 ( ) ) {
ipr . Address . Ipv4 . sin_family = AF_INET ;
ipr . Address . Ipv4 . sin_addr . S_un . S_addr = * ( ( const uint32_t * ) ip . rawIpData ( ) ) ;
ipr . OnLinkPrefixLength = ip . port ( ) ;
2014-04-08 23:10:48 +00:00
if ( ipr . OnLinkPrefixLength > = 32 )
return false ;
2014-01-23 22:15:00 +00:00
} else if ( ip . isV6 ( ) ) {
2014-04-08 19:00:21 +00:00
ipr . Address . Ipv6 . sin6_family = AF_INET6 ;
memcpy ( ipr . Address . Ipv6 . sin6_addr . u . Byte , ip . rawIpData ( ) , 16 ) ;
ipr . OnLinkPrefixLength = ip . port ( ) ;
2014-04-08 23:10:48 +00:00
if ( ipr . OnLinkPrefixLength > = 128 )
return false ;
2014-01-23 22:15:00 +00:00
} else return false ;
ipr . PrefixOrigin = IpPrefixOriginManual ;
ipr . SuffixOrigin = IpSuffixOriginManual ;
ipr . ValidLifetime = 0xffffffff ;
ipr . PreferredLifetime = 0xffffffff ;
2014-08-05 04:48:59 +00:00
ipr . InterfaceLuid = _deviceLuid ;
ipr . InterfaceIndex = _getDeviceIndex ( ) ;
2014-01-23 22:15:00 +00:00
2015-04-24 22:05:28 +00:00
if ( CreateUnicastIpAddressEntry ( & ipr ) ! = NO_ERROR )
2014-01-23 22:15:00 +00:00
return false ;
2013-08-26 21:22:20 +00:00
}
2014-08-13 00:20:34 +00:00
std : : vector < std : : string > regIps ( _getRegistryIPv4Value ( " IPAddress " ) ) ;
if ( std : : find ( regIps . begin ( ) , regIps . end ( ) , ip . toIpString ( ) ) = = regIps . end ( ) ) {
std : : vector < std : : string > regSubnetMasks ( _getRegistryIPv4Value ( " SubnetMask " ) ) ;
regIps . push_back ( ip . toIpString ( ) ) ;
regSubnetMasks . push_back ( ip . netmask ( ) . toIpString ( ) ) ;
_setRegistryIPv4Value ( " IPAddress " , regIps ) ;
_setRegistryIPv4Value ( " SubnetMask " , regSubnetMasks ) ;
}
2014-01-23 22:15:00 +00:00
} catch ( . . . ) {
2014-04-08 23:10:48 +00:00
return false ;
2014-01-23 22:15:00 +00:00
}
2014-04-08 23:10:48 +00:00
return true ;
2013-08-14 17:23:25 +00:00
}
2015-04-24 22:05:28 +00:00
bool WindowsEthernetTap : : removeIp ( const InetAddress & ip )
2013-08-14 17:23:25 +00:00
{
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
return false ;
2013-08-26 21:22:20 +00:00
try {
MIB_UNICASTIPADDRESS_TABLE * ipt = ( MIB_UNICASTIPADDRESS_TABLE * ) 0 ;
if ( GetUnicastIpAddressTable ( AF_UNSPEC , & ipt ) = = NO_ERROR ) {
for ( DWORD i = 0 ; i < ipt - > NumEntries ; + + i ) {
2014-08-05 04:48:59 +00:00
if ( ipt - > Table [ i ] . InterfaceLuid . Value = = _deviceLuid . Value ) {
2013-08-26 21:22:20 +00:00
InetAddress addr ;
switch ( ipt - > Table [ i ] . Address . si_family ) {
case AF_INET :
addr . set ( & ( ipt - > Table [ i ] . Address . Ipv4 . sin_addr . S_un . S_addr ) , 4 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
break ;
case AF_INET6 :
addr . set ( ipt - > Table [ i ] . Address . Ipv6 . sin6_addr . u . Byte , 16 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
2015-04-24 22:05:28 +00:00
if ( addr . ipScope ( ) = = InetAddress : : IP_SCOPE_LINK_LOCAL )
2014-01-23 22:15:00 +00:00
continue ; // can't remove link-local IPv6 addresses
2013-08-26 21:22:20 +00:00
break ;
}
if ( addr = = ip ) {
DeleteUnicastIpAddressEntry ( & ( ipt - > Table [ i ] ) ) ;
2013-08-27 15:15:14 +00:00
FreeMibTable ( ipt ) ;
2014-08-13 00:20:34 +00:00
std : : vector < std : : string > regIps ( _getRegistryIPv4Value ( " IPAddress " ) ) ;
std : : vector < std : : string > regSubnetMasks ( _getRegistryIPv4Value ( " SubnetMask " ) ) ;
std : : string ipstr ( ip . toIpString ( ) ) ;
for ( std : : vector < std : : string > : : iterator rip ( regIps . begin ( ) ) , rm ( regSubnetMasks . begin ( ) ) ; ( ( rip ! = regIps . end ( ) ) & & ( rm ! = regSubnetMasks . end ( ) ) ) ; + + rip , + + rm ) {
if ( * rip = = ipstr ) {
regIps . erase ( rip ) ;
regSubnetMasks . erase ( rm ) ;
_setRegistryIPv4Value ( " IPAddress " , regIps ) ;
_setRegistryIPv4Value ( " SubnetMask " , regSubnetMasks ) ;
break ;
}
}
2013-08-26 21:22:20 +00:00
return true ;
}
}
}
2014-01-23 22:15:00 +00:00
FreeMibTable ( ( PVOID ) ipt ) ;
2013-08-26 21:22:20 +00:00
}
2014-08-05 04:48:59 +00:00
} catch ( . . . ) { }
2013-08-14 17:23:25 +00:00
return false ;
}
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > WindowsEthernetTap : : ips ( ) const
2013-08-26 21:22:20 +00:00
{
2014-01-23 22:15:00 +00:00
static const InetAddress linkLocalLoopback ( " fe80::1 " , 64 ) ; // what is this and why does Windows assign it?
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > addrs ;
2013-08-26 21:22:20 +00:00
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
return addrs ;
2013-08-26 21:22:20 +00:00
try {
MIB_UNICASTIPADDRESS_TABLE * ipt = ( MIB_UNICASTIPADDRESS_TABLE * ) 0 ;
if ( GetUnicastIpAddressTable ( AF_UNSPEC , & ipt ) = = NO_ERROR ) {
for ( DWORD i = 0 ; i < ipt - > NumEntries ; + + i ) {
2014-08-05 04:48:59 +00:00
if ( ipt - > Table [ i ] . InterfaceLuid . Value = = _deviceLuid . Value ) {
2013-08-26 21:22:20 +00:00
switch ( ipt - > Table [ i ] . Address . si_family ) {
2014-01-23 22:15:00 +00:00
case AF_INET : {
InetAddress ip ( & ( ipt - > Table [ i ] . Address . Ipv4 . sin_addr . S_un . S_addr ) , 4 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
if ( ip ! = InetAddress : : LO4 )
2015-04-24 22:05:28 +00:00
addrs . push_back ( ip ) ;
2014-01-23 22:15:00 +00:00
} break ;
2013-08-27 20:45:22 +00:00
case AF_INET6 : {
InetAddress ip ( ipt - > Table [ i ] . Address . Ipv6 . sin6_addr . u . Byte , 16 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
2014-01-23 22:15:00 +00:00
if ( ( ip ! = linkLocalLoopback ) & & ( ip ! = InetAddress : : LO6 ) )
2015-04-24 22:05:28 +00:00
addrs . push_back ( ip ) ;
2013-08-27 20:45:22 +00:00
} break ;
2013-08-26 21:22:20 +00:00
}
}
}
2013-08-27 15:15:14 +00:00
FreeMibTable ( ipt ) ;
2013-08-26 21:22:20 +00:00
}
2014-04-08 19:00:21 +00:00
} catch ( . . . ) { } // sanity check, shouldn't happen unless out of memory
2013-08-26 21:22:20 +00:00
2015-04-24 22:05:28 +00:00
std : : sort ( addrs . begin ( ) , addrs . end ( ) ) ;
std : : unique ( addrs . begin ( ) , addrs . end ( ) ) ;
2013-08-26 21:22:20 +00:00
return addrs ;
}
2014-04-08 19:00:21 +00:00
void WindowsEthernetTap : : put ( const MAC & from , const MAC & to , unsigned int etherType , const void * data , unsigned int len )
2013-08-14 17:23:25 +00:00
{
2014-04-08 23:10:48 +00:00
if ( ( ! _initialized ) | | ( ! _enabled ) | | ( _tap = = INVALID_HANDLE_VALUE ) | | ( len > ( ZT_IF_MTU ) ) )
2014-01-28 07:13:36 +00:00
return ;
2014-08-05 04:48:59 +00:00
Mutex : : Lock _l ( _injectPending_m ) ;
_injectPending . push ( std : : pair < Array < char , ZT_IF_MTU + 32 > , unsigned int > ( Array < char , ZT_IF_MTU + 32 > ( ) , len + 14 ) ) ;
char * d = _injectPending . back ( ) . first . data ;
to . copyTo ( d , 6 ) ;
from . copyTo ( d + 6 , 6 ) ;
d [ 12 ] = ( char ) ( ( etherType > > 8 ) & 0xff ) ;
d [ 13 ] = ( char ) ( etherType & 0xff ) ;
memcpy ( d + 14 , data , len ) ;
2013-08-25 22:18:02 +00:00
ReleaseSemaphore ( _injectSemaphore , 1 , NULL ) ;
2013-08-14 17:23:25 +00:00
}
2014-04-08 19:00:21 +00:00
std : : string WindowsEthernetTap : : deviceName ( ) const
2013-08-14 17:23:25 +00:00
{
2014-08-05 04:48:59 +00:00
char tmp [ 1024 ] ;
if ( ConvertInterfaceLuidToNameA ( & _deviceLuid , tmp , sizeof ( tmp ) ) ! = NO_ERROR )
return std : : string ( " [ConvertInterfaceLuidToName() failed] " ) ;
return std : : string ( tmp ) ;
2013-08-14 17:23:25 +00:00
}
2014-08-05 04:48:59 +00:00
void WindowsEthernetTap : : setFriendlyName ( const char * dn )
2014-01-27 06:47:08 +00:00
{
2014-08-05 04:48:59 +00:00
if ( ! _initialized )
return ;
HKEY ifp ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , ( std : : string ( " SYSTEM \\ CurrentControlSet \\ Control \\ Network \\ {4D36E972-E325-11CE-BFC1-08002BE10318} \\ " ) + _netCfgInstanceId ) . c_str ( ) , 0 , KEY_READ | KEY_WRITE , & ifp ) = = ERROR_SUCCESS ) {
RegSetKeyValueA ( ifp , " Connection " , " Name " , REG_SZ , ( LPCVOID ) dn , ( DWORD ) ( strlen ( dn ) + 1 ) ) ;
RegCloseKey ( ifp ) ;
}
2014-01-27 06:47:08 +00:00
}
2015-04-24 22:05:28 +00:00
void WindowsEthernetTap : : scanMulticastGroups ( std : : vector < MulticastGroup > & added , std : : vector < MulticastGroup > & removed )
2013-08-14 17:23:25 +00:00
{
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
2015-04-24 22:05:28 +00:00
return ;
2014-04-08 22:47:33 +00:00
HANDLE t = _tap ;
if ( t = = INVALID_HANDLE_VALUE )
2015-04-24 22:05:28 +00:00
return ;
2013-08-27 20:45:22 +00:00
2015-04-24 22:05:28 +00:00
std : : vector < MulticastGroup > newGroups ;
2013-08-27 20:45:22 +00:00
2014-01-21 21:07:22 +00:00
// The ZT1 tap driver supports an IOCTL to get multicast memberships at the L2
// level... something Windows does not seem to expose ordinarily. This lets
// pretty much anything work... IPv4, IPv6, IPX, oldskool Netbios, who knows...
unsigned char mcastbuf [ TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE ] ;
DWORD bytesReturned = 0 ;
2014-04-08 22:47:33 +00:00
if ( DeviceIoControl ( t , TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS , ( LPVOID ) 0 , 0 , ( LPVOID ) mcastbuf , sizeof ( mcastbuf ) , & bytesReturned , NULL ) ) {
2014-01-21 21:07:22 +00:00
MAC mac ;
DWORD i = 0 ;
while ( ( i + 6 ) < = bytesReturned ) {
2014-05-23 23:21:57 +00:00
mac . setTo ( mcastbuf + i , 6 ) ;
i + = 6 ;
if ( ( mac . isMulticast ( ) ) & & ( ! mac . isBroadcast ( ) ) ) {
2014-01-23 07:46:33 +00:00
// exclude the nulls that may be returned or any other junk Windows puts in there
2015-04-24 22:05:28 +00:00
newGroups . push_back ( MulticastGroup ( mac , 0 ) ) ;
2014-01-21 21:07:22 +00:00
}
}
}
2013-08-27 20:45:22 +00:00
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > allIps ( ips ( ) ) ;
for ( std : : vector < InetAddress > : : iterator ip ( allIps . begin ( ) ) ; ip ! = allIps . end ( ) ; + + ip )
newGroups . push_back ( MulticastGroup : : deriveMulticastGroupForAddressResolution ( * ip ) ) ;
2014-01-21 21:07:22 +00:00
2015-04-24 22:05:28 +00:00
std : : sort ( newGroups . begin ( ) , newGroups . end ( ) ) ;
std : : unique ( newGroups . begin ( ) , newGroups . end ( ) ) ;
for ( std : : vector < MulticastGroup > : : iterator m ( newGroups . begin ( ) ) ; m ! = newGroups . end ( ) ; + + m ) {
if ( ! std : : binary_search ( _multicastGroups . begin ( ) , _multicastGroups . end ( ) , * m ) )
added . push_back ( * m ) ;
2013-08-27 20:45:22 +00:00
}
2015-04-24 22:05:28 +00:00
for ( std : : vector < MulticastGroup > : : iterator m ( _multicastGroups . begin ( ) ) ; m ! = _multicastGroups . end ( ) ; + + m ) {
if ( ! std : : binary_search ( newGroups . begin ( ) , newGroups . end ( ) , * m ) )
removed . push_back ( * m ) ;
2013-08-27 20:45:22 +00:00
}
2015-04-24 22:05:28 +00:00
_multicastGroups . swap ( newGroups ) ;
2014-10-03 18:59:50 +00:00
}
2014-04-08 19:00:21 +00:00
void WindowsEthernetTap : : threadMain ( )
2013-08-23 21:39:21 +00:00
throw ( )
2013-08-14 17:23:25 +00:00
{
2015-06-12 14:02:04 +00:00
char tapReadBuf [ ZT_IF_MTU + 32 ] ;
char tapPath [ 128 ] ;
2013-08-25 22:18:02 +00:00
HANDLE wait4 [ 3 ] ;
2015-06-12 14:02:04 +00:00
OVERLAPPED tapOvlRead , tapOvlWrite ;
2014-04-08 22:47:33 +00:00
Utils : : snprintf ( tapPath , sizeof ( tapPath ) , " \\ \\ . \\ Global \\ %s.tap " , _netCfgInstanceId . c_str ( ) ) ;
2015-06-12 14:02:04 +00:00
try {
while ( _run ) {
2015-05-20 21:21:14 +00:00
_enableTapDevice ( ) ;
Sleep ( 500 ) ;
2015-05-20 01:13:20 +00:00
2015-06-12 14:02:04 +00:00
_tap = CreateFileA ( tapPath , GENERIC_READ | GENERIC_WRITE , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED , NULL ) ;
if ( _tap = = INVALID_HANDLE_VALUE ) {
_disableTapDevice ( ) ;
_enableTapDevice ( ) ;
Sleep ( 1000 ) ;
continue ;
}
2014-04-08 22:47:33 +00:00
2015-06-12 14:02:04 +00:00
{
uint32_t tmpi = 1 ;
DWORD bytesReturned = 0 ;
DeviceIoControl ( _tap , TAP_WIN_IOCTL_SET_MEDIA_STATUS , & tmpi , sizeof ( tmpi ) , & tmpi , sizeof ( tmpi ) , & bytesReturned , NULL ) ;
}
2014-08-22 00:49:05 +00:00
# ifdef ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
2015-06-12 14:02:04 +00:00
{
/* This inserts a fake default route and a fake ARP entry, forcing
* Windows to detect this as a " real " network and apply proper
* firewall rules .
*
* This hack is completely stupid , but Windows made me do it
* by being broken and insane .
*
* Background : Windows tries to detect its network location by
* matching it to the ARP address of the default route . Networks
* without default routes are " unidentified networks " and cannot
* have their firewall classification changed by the user ( easily ) .
*
* Yes , you read that right .
*
* The common workaround is to set * NdisDeviceType to 1 , which
* totally disables all Windows firewall functionality . This is
* the answer you ' ll find on most forums for things like OpenVPN .
*
* Yes , you read that right .
*
* The default route workaround is also known , but for this to
* work there must be a known default IP that resolves to a known
* ARP address . This works for an OpenVPN tunnel , but not here
* because this isn ' t a tunnel . It ' s a mesh . There is no " other
* end , " or any other known always on IP.
*
* So let ' s make a fake one and shove it in there along with its
* fake static ARP entry . Also makes it instant - on and static .
*
* We ' ll have to see what DHCP does with this . In the future we
* probably will not want to do this on DHCP - enabled networks , so
* when we enable DHCP we will go in and yank this wacko hacko from
* the routing table before doing so .
*
* Like Jesse Pinkman would say : " YEEEEAAH BITCH! " */
const uint32_t fakeIp = htonl ( 0x19fffffe ) ; // 25.255.255.254 -- unrouted IPv4 block
for ( int i = 0 ; i < 8 ; + + i ) {
MIB_IPNET_ROW2 ipnr ;
memset ( & ipnr , 0 , sizeof ( ipnr ) ) ;
ipnr . Address . si_family = AF_INET ;
ipnr . Address . Ipv4 . sin_addr . s_addr = fakeIp ;
ipnr . InterfaceLuid . Value = _deviceLuid . Value ;
ipnr . PhysicalAddress [ 0 ] = _mac [ 0 ] ^ 0x10 ; // just make something up that's consistent and not part of this net
ipnr . PhysicalAddress [ 1 ] = 0x00 ;
ipnr . PhysicalAddress [ 2 ] = ( UCHAR ) ( ( _deviceGuid . Data1 > > 24 ) & 0xff ) ;
ipnr . PhysicalAddress [ 3 ] = ( UCHAR ) ( ( _deviceGuid . Data1 > > 16 ) & 0xff ) ;
ipnr . PhysicalAddress [ 4 ] = ( UCHAR ) ( ( _deviceGuid . Data1 > > 8 ) & 0xff ) ;
ipnr . PhysicalAddress [ 5 ] = ( UCHAR ) ( _deviceGuid . Data1 & 0xff ) ;
ipnr . PhysicalAddressLength = 6 ;
ipnr . State = NlnsPermanent ;
ipnr . IsRouter = 1 ;
ipnr . IsUnreachable = 0 ;
ipnr . ReachabilityTime . LastReachable = 0x0fffffff ;
ipnr . ReachabilityTime . LastUnreachable = 1 ;
DWORD result = CreateIpNetEntry2 ( & ipnr ) ;
if ( result ! = NO_ERROR )
Sleep ( 500 ) ;
else break ;
}
for ( int i = 0 ; i < 8 ; + + i ) {
MIB_IPFORWARD_ROW2 nr ;
memset ( & nr , 0 , sizeof ( nr ) ) ;
InitializeIpForwardEntry ( & nr ) ;
nr . InterfaceLuid . Value = _deviceLuid . Value ;
nr . DestinationPrefix . Prefix . si_family = AF_INET ; // rest is left as 0.0.0.0/0
nr . NextHop . si_family = AF_INET ;
nr . NextHop . Ipv4 . sin_addr . s_addr = fakeIp ;
nr . Metric = 9999 ; // do not use as real default route
nr . Protocol = MIB_IPPROTO_NETMGMT ;
DWORD result = CreateIpForwardEntry2 ( & nr ) ;
if ( result ! = NO_ERROR )
Sleep ( 500 ) ;
else break ;
}
2014-08-22 00:49:05 +00:00
}
# endif
2015-05-08 23:31:50 +00:00
2015-06-12 14:02:04 +00:00
memset ( & tapOvlRead , 0 , sizeof ( tapOvlRead ) ) ;
tapOvlRead . hEvent = CreateEvent ( NULL , TRUE , FALSE , NULL ) ;
memset ( & tapOvlWrite , 0 , sizeof ( tapOvlWrite ) ) ;
tapOvlWrite . hEvent = CreateEvent ( NULL , TRUE , FALSE , NULL ) ;
wait4 [ 0 ] = _injectSemaphore ;
wait4 [ 1 ] = tapOvlRead . hEvent ;
wait4 [ 2 ] = tapOvlWrite . hEvent ; // only included if writeInProgress is true
ReadFile ( _tap , tapReadBuf , sizeof ( tapReadBuf ) , NULL , & tapOvlRead ) ;
bool writeInProgress = false ;
ULONGLONG timeOfLastBorkCheck = GetTickCount64 ( ) ;
while ( _run ) {
DWORD waitResult = WaitForMultipleObjectsEx ( writeInProgress ? 3 : 2 , wait4 , FALSE , 2500 , TRUE ) ;
if ( ! _run ) break ; // will also break outer while(_run)
// Check for issues with adapter and close/reopen if any are detected. This
// check fixes a while boatload of Windows adapter 'coma' issues after
// sleep/wake and when adapters are added/removed. Basically if the tap
// device is borked, whack it.
{
ULONGLONG tc = GetTickCount64 ( ) ;
if ( ( tc - timeOfLastBorkCheck ) > = 2500 ) {
timeOfLastBorkCheck = tc ;
2015-07-07 00:58:04 +00:00
char aabuf [ 16384 ] ;
ULONG aalen = sizeof ( aabuf ) ;
if ( GetAdaptersAddresses ( AF_UNSPEC , GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME , ( void * ) 0 , reinterpret_cast < PIP_ADAPTER_ADDRESSES > ( aabuf ) , & aalen ) = = NO_ERROR ) {
2015-06-12 14:02:04 +00:00
bool isBorked = false ;
2015-07-07 00:58:04 +00:00
PIP_ADAPTER_ADDRESSES aa = reinterpret_cast < PIP_ADAPTER_ADDRESSES > ( aabuf ) ;
while ( aa ) {
if ( _deviceLuid . Value = = aa - > Luid . Value ) {
isBorked = ( aa - > OperStatus ! = IfOperStatusUp ) ;
2015-06-12 14:02:04 +00:00
break ;
}
2015-07-07 00:58:04 +00:00
aa = aa - > Next ;
2015-06-12 14:02:04 +00:00
}
2015-07-07 00:58:04 +00:00
2015-06-12 14:02:04 +00:00
if ( isBorked ) {
// Close and reopen tap device if there's an issue (outer loop)
break ;
}
}
2015-05-08 23:31:50 +00:00
}
2013-08-25 22:18:02 +00:00
}
2015-06-12 14:02:04 +00:00
if ( ( waitResult = = WAIT_TIMEOUT ) | | ( waitResult = = WAIT_FAILED ) )
continue ;
if ( HasOverlappedIoCompleted ( & tapOvlRead ) ) {
DWORD bytesRead = 0 ;
if ( GetOverlappedResult ( _tap , & tapOvlRead , & bytesRead , FALSE ) ) {
if ( ( bytesRead > 14 ) & & ( _enabled ) ) {
MAC to ( tapReadBuf , 6 ) ;
MAC from ( tapReadBuf + 6 , 6 ) ;
unsigned int etherType = ( ( ( ( unsigned int ) tapReadBuf [ 12 ] ) & 0xff ) < < 8 ) | ( ( ( unsigned int ) tapReadBuf [ 13 ] ) & 0xff ) ;
try {
// TODO: decode vlans
_handler ( _arg , _nwid , from , to , etherType , 0 , tapReadBuf + 14 , bytesRead - 14 ) ;
} catch ( . . . ) { } // handlers should not throw
}
}
ReadFile ( _tap , tapReadBuf , ZT_IF_MTU + 32 , NULL , & tapOvlRead ) ;
}
2015-05-08 23:31:50 +00:00
2015-06-12 14:02:04 +00:00
if ( writeInProgress ) {
if ( HasOverlappedIoCompleted ( & tapOvlWrite ) ) {
writeInProgress = false ;
_injectPending_m . lock ( ) ;
_injectPending . pop ( ) ;
} else continue ; // still writing, so skip code below and wait
} else _injectPending_m . lock ( ) ;
if ( ! _injectPending . empty ( ) ) {
WriteFile ( _tap , _injectPending . front ( ) . first . data , _injectPending . front ( ) . second , NULL , & tapOvlWrite ) ;
writeInProgress = true ;
}
2013-08-25 22:18:02 +00:00
2015-06-12 14:02:04 +00:00
_injectPending_m . unlock ( ) ;
}
2013-08-25 22:18:02 +00:00
2015-06-12 14:02:04 +00:00
CancelIo ( _tap ) ;
2014-04-08 22:47:33 +00:00
2015-06-12 14:02:04 +00:00
CloseHandle ( tapOvlRead . hEvent ) ;
CloseHandle ( tapOvlWrite . hEvent ) ;
CloseHandle ( _tap ) ;
_tap = INVALID_HANDLE_VALUE ;
2014-04-08 22:47:33 +00:00
2015-06-12 14:02:04 +00:00
// We will restart and re-open the tap unless _run == false
}
} catch ( . . . ) { } // catch unexpected exceptions -- this should not happen but would prevent program crash or other weird issues since threads should not throw
2013-08-14 17:23:25 +00:00
}
2015-04-24 22:05:28 +00:00
void WindowsEthernetTap : : destroyAllPersistentTapDevices ( const char * pathToHelpers )
{
char subkeyName [ 4096 ] ;
char subkeyClass [ 4096 ] ;
char data [ 4096 ] ;
std : : set < std : : string > instanceIdPathsToRemove ;
{
HKEY nwAdapters ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Control \\ Class \\ {4D36E972-E325-11CE-BFC1-08002BE10318} " , 0 , KEY_READ | KEY_WRITE , & nwAdapters ) ! = ERROR_SUCCESS )
return ;
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
DWORD type ;
DWORD dataLen ;
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ! strnicmp ( data , " zttap " , 5 ) ) {
std : : string instanceIdPath ;
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
instanceIdPath . assign ( data , dataLen ) ;
if ( instanceIdPath . length ( ) ! = 0 )
instanceIdPathsToRemove . insert ( instanceIdPath ) ;
}
}
} else break ; // end of list or failure
}
RegCloseKey ( nwAdapters ) ;
}
for ( std : : set < std : : string > : : iterator iidp ( instanceIdPathsToRemove . begin ( ) ) ; iidp ! = instanceIdPathsToRemove . end ( ) ; + + iidp )
2015-04-24 23:31:19 +00:00
deletePersistentTapDevice ( pathToHelpers , iidp - > c_str ( ) ) ;
}
void WindowsEthernetTap : : deletePersistentTapDevice ( const char * pathToHelpers , const char * instanceId )
{
HANDLE devconLog = CreateFileA ( ( std : : string ( pathToHelpers ) + " \\ devcon.log " ) . c_str ( ) , GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
STARTUPINFOA startupInfo ;
startupInfo . cb = sizeof ( startupInfo ) ;
if ( devconLog ! = INVALID_HANDLE_VALUE ) {
SetFilePointer ( devconLog , 0 , 0 , FILE_END ) ;
startupInfo . hStdOutput = devconLog ;
startupInfo . hStdError = devconLog ;
}
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
if ( CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + pathToHelpers + WINENV . devcon + " \" remove @ " + instanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
}
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
2015-04-24 22:05:28 +00:00
}
2014-08-05 04:48:59 +00:00
bool WindowsEthernetTap : : _disableTapDevice ( )
2014-01-27 06:47:08 +00:00
{
2014-08-05 04:48:59 +00:00
HANDLE devconLog = CreateFileA ( ( _pathToHelpers + " \\ devcon.log " ) . c_str ( ) , GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( devconLog ! = INVALID_HANDLE_VALUE )
SetFilePointer ( devconLog , 0 , 0 , FILE_END ) ;
2014-02-28 23:04:50 +00:00
2014-01-27 06:47:08 +00:00
STARTUPINFOA startupInfo ;
startupInfo . cb = sizeof ( startupInfo ) ;
2014-03-06 22:06:31 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE ) {
startupInfo . hStdOutput = devconLog ;
startupInfo . hStdError = devconLog ;
}
2014-01-27 06:47:08 +00:00
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
2015-04-24 22:05:28 +00:00
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WINENV . devcon + " \" disable @ " + _deviceInstanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
2014-03-06 22:06:31 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
2014-08-05 04:48:59 +00:00
return false ;
2014-01-27 06:47:08 +00:00
}
2014-08-05 04:48:59 +00:00
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
2014-03-06 22:06:31 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
2014-01-27 06:47:08 +00:00
2014-08-05 04:48:59 +00:00
return true ;
2014-01-27 06:47:08 +00:00
}
2014-08-05 04:48:59 +00:00
bool WindowsEthernetTap : : _enableTapDevice ( )
2014-02-28 23:04:50 +00:00
{
2014-08-05 04:48:59 +00:00
HANDLE devconLog = CreateFileA ( ( _pathToHelpers + " \\ devcon.log " ) . c_str ( ) , GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( devconLog ! = INVALID_HANDLE_VALUE )
SetFilePointer ( devconLog , 0 , 0 , FILE_END ) ;
2014-02-28 23:04:50 +00:00
2014-08-05 04:48:59 +00:00
STARTUPINFOA startupInfo ;
startupInfo . cb = sizeof ( startupInfo ) ;
if ( devconLog ! = INVALID_HANDLE_VALUE ) {
startupInfo . hStdOutput = devconLog ;
startupInfo . hStdError = devconLog ;
}
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
2015-04-24 22:05:28 +00:00
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WINENV . devcon + " \" enable @ " + _deviceInstanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
2014-08-05 04:48:59 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
return false ;
}
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
2014-02-28 23:04:50 +00:00
2014-08-05 04:48:59 +00:00
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
2014-02-28 23:04:50 +00:00
2014-08-05 04:48:59 +00:00
return true ;
}
2014-02-28 23:04:50 +00:00
2014-08-05 04:48:59 +00:00
NET_IFINDEX WindowsEthernetTap : : _getDeviceIndex ( )
{
MIB_IF_TABLE2 * ift = ( MIB_IF_TABLE2 * ) 0 ;
if ( GetIfTable2Ex ( MibIfTableRaw , & ift ) ! = NO_ERROR )
throw std : : runtime_error ( " GetIfTable2Ex() failed " ) ;
2014-02-28 23:04:50 +00:00
2014-08-05 04:48:59 +00:00
for ( ULONG i = 0 ; i < ift - > NumEntries ; + + i ) {
if ( ift - > Table [ i ] . InterfaceLuid . Value = = _deviceLuid . Value ) {
2014-08-08 21:57:13 +00:00
NET_IFINDEX idx = ift - > Table [ i ] . InterfaceIndex ;
2014-08-05 04:48:59 +00:00
FreeMibTable ( ift ) ;
2014-08-08 21:57:13 +00:00
return idx ;
2014-08-05 04:48:59 +00:00
}
2014-02-28 23:04:50 +00:00
}
2014-08-05 04:48:59 +00:00
FreeMibTable ( & ift ) ;
throw std : : runtime_error ( " interface not found " ) ;
2014-02-28 23:04:50 +00:00
}
2014-08-13 00:20:34 +00:00
std : : vector < std : : string > WindowsEthernetTap : : _getRegistryIPv4Value ( const char * regKey )
{
std : : vector < std : : string > value ;
HKEY tcpIpInterfaces ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ services \\ Tcpip \\ Parameters \\ Interfaces " , 0 , KEY_READ | KEY_WRITE , & tcpIpInterfaces ) = = ERROR_SUCCESS ) {
char buf [ 16384 ] ;
DWORD len = sizeof ( buf ) ;
DWORD kt = REG_MULTI_SZ ;
if ( RegGetValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , regKey , 0 , & kt , & buf , & len ) = = ERROR_SUCCESS ) {
switch ( kt ) {
case REG_SZ :
if ( len > 0 )
value . push_back ( std : : string ( buf ) ) ;
break ;
case REG_MULTI_SZ : {
for ( DWORD k = 0 , s = 0 ; k < len ; + + k ) {
if ( ! buf [ k ] ) {
if ( s < k ) {
value . push_back ( std : : string ( buf + s ) ) ;
s = k + 1 ;
} else break ;
}
}
} break ;
}
}
RegCloseKey ( tcpIpInterfaces ) ;
}
return value ;
}
void WindowsEthernetTap : : _setRegistryIPv4Value ( const char * regKey , const std : : vector < std : : string > & value )
{
std : : string regMulti ;
for ( std : : vector < std : : string > : : const_iterator s ( value . begin ( ) ) ; s ! = value . end ( ) ; + + s ) {
regMulti . append ( * s ) ;
regMulti . push_back ( ( char ) 0 ) ;
}
HKEY tcpIpInterfaces ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ services \\ Tcpip \\ Parameters \\ Interfaces " , 0 , KEY_READ | KEY_WRITE , & tcpIpInterfaces ) = = ERROR_SUCCESS ) {
if ( regMulti . length ( ) > 0 ) {
regMulti . push_back ( ( char ) 0 ) ;
RegSetKeyValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , regKey , REG_MULTI_SZ , regMulti . data ( ) , ( DWORD ) regMulti . length ( ) ) ;
} else {
RegDeleteKeyValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , regKey ) ;
}
RegCloseKey ( tcpIpInterfaces ) ;
}
}
2013-08-14 17:23:25 +00:00
} // namespace ZeroTier