@ -44,42 +44,71 @@
# include <nldef.h>
# include <iostream>
# include <set>
# include "../node/Constants.hpp"
# include "../node/Utils.hpp"
# include "../node/Mutex.hpp"
# include "WindowsEthernetTap.hpp"
# include "OSUtils.hpp"
# include "..\windows\TapDriver\tap-windows.h"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier : : MulticastGroup _blindWildcardMulticastGroup ( ZeroTier : : MAC ( 0xff ) , 0 ) ;
//static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
# define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
namespace ZeroTier {
namespace {
class WindowsEthernetTapEnv
{
public :
WindowsEthernetTapEnv ( )
{
# ifdef _WIN64
is64Bit = TRUE ;
devcon = " \\ devcon_x64.exe " ;
tapDriver = " \\ tap-windows \\ x64 \\ zttap200.inf " ;
# else
is64Bit = FALSE ;
IsWow64Process ( GetCurrentProcess ( ) , & is64Bit ) ;
devcon = ( ( is64Bit = = TRUE ) ? " \\ devcon_x64.exe " : " \\ devcon_x86.exe " ) ;
tapDriver = ( ( is64Bit = = TRUE ) ? " \\ tap-windows \\ x64 \\ zttap200.inf " : " \\ tap-windows \\ x86 \\ zttap200.inf " ) ;
# endif
}
BOOL is64Bit ;
std : : string devcon ;
std : : string tapDriver ;
} ;
static const WindowsEthernetTapEnv WINENV ;
} // anonymous namespace
// Only create or delete devices one at a time
static Mutex _systemTapInitLock ;
WindowsEthernetTap : : WindowsEthernetTap (
const char * pathToHelpers ,
const char * hp ,
const MAC & mac ,
unsigned int mtu ,
unsigned int metric ,
uint64_t nwid ,
const char * desiredDevice ,
const char * friendlyName ,
void ( * handler ) ( void * , const MAC & , const MAC & , unsigned int , const Buffer < 4096 > & ) ,
void ( * handler ) ( void * , uint64_t , const MAC & , const MAC & , unsigned int , unsigned int , const void * , unsigned int ) ,
void * arg ) :
EthernetTap ( " WindowsEthernetTap " , mac , mtu , metric ) ,
_handler ( handler ) ,
_arg ( arg ) ,
_mac ( mac ) ,
_nwid ( nwid ) ,
_tap ( INVALID_HANDLE_VALUE ) ,
_injectSemaphore ( INVALID_HANDLE_VALUE ) ,
_pathToHelpers ( pathToHelpers ) ,
_pathToHelpers ( hp ) ,
_run ( true ) ,
_initialized ( false ) ,
_enabled ( true )
@ -169,11 +198,11 @@ WindowsEthernetTap::WindowsEthernetTap(
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WindowsEthernetTapFactory : : W INENV . devcon + " \" install \" " + _pathToHelpers + WindowsEthernetTapFactory : : WINENV . tapDriver + " \" zttap200 " ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WINENV . devcon + " \" install \" " + _pathToHelpers + WINENV . tapDriver + " \" zttap200 " ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
RegCloseKey ( nwAdapters ) ;
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
throw std : : runtime_error ( std : : string ( " unable to find or execute devcon at " ) + WindowsEthernetTapFactory : : W INENV . devcon ) ;
throw std : : runtime_error ( std : : string ( " unable to find or execute devcon at " ) + WINENV . devcon ) ;
}
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
@ -296,18 +325,18 @@ bool WindowsEthernetTap::enabled() const
return _enabled ;
}
bool WindowsEthernetTap : : addIP ( const InetAddress & ip )
bool WindowsEthernetTap : : addIp ( const InetAddress & ip )
{
if ( ! _initialized )
return false ;
if ( ! ip . netmaskBits ( ) ) // sanity check... netmask of 0.0.0.0 is WUT?
return false ;
std : : set < InetAddress > haveIps ( ips ( ) ) ;
std : : vector < InetAddress > haveIps ( ips ( ) ) ;
try {
// Add IP to interface at the netlink level if not already assigned.
if ( ! haveIps . count ( ip ) ) {
if ( ! std : : binary_search ( haveIps . begin ( ) , haveIps . end ( ) , ip ) ) {
MIB_UNICASTIPADDRESS_ROW ipr ;
InitializeUnicastIpAddressEntry ( & ipr ) ;
@ -333,11 +362,8 @@ bool WindowsEthernetTap::addIP(const InetAddress &ip)
ipr . InterfaceLuid = _deviceLuid ;
ipr . InterfaceIndex = _getDeviceIndex ( ) ;
if ( CreateUnicastIpAddressEntry ( & ipr ) = = NO_ERROR ) {
haveIps . insert ( ip ) ;
} else {
if ( CreateUnicastIpAddressEntry ( & ipr ) ! = NO_ERROR )
return false ;
}
}
std : : vector < std : : string > regIps ( _getRegistryIPv4Value ( " IPAddress " ) ) ;
@ -348,14 +374,13 @@ bool WindowsEthernetTap::addIP(const InetAddress &ip)
_setRegistryIPv4Value ( " IPAddress " , regIps ) ;
_setRegistryIPv4Value ( " SubnetMask " , regSubnetMasks ) ;
}
//_syncIpsWithRegistry(haveIps,_netCfgInstanceId);
} catch ( . . . ) {
return false ;
}
return true ;
}
bool WindowsEthernetTap : : removeIP ( const InetAddress & ip )
bool WindowsEthernetTap : : removeIp ( const InetAddress & ip )
{
if ( ! _initialized )
return false ;
@ -371,7 +396,7 @@ bool WindowsEthernetTap::removeIP(const InetAddress &ip)
break ;
case AF_INET6 :
addr . set ( ipt - > Table [ i ] . Address . Ipv6 . sin6_addr . u . Byte , 16 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
if ( addr . isLinkLocal ( ) )
if ( addr . ipScope ( ) = = InetAddress : : IP_SCOPE_LINK_LOCAL )
continue ; // can't remove link-local IPv6 addresses
break ;
}
@ -402,10 +427,10 @@ bool WindowsEthernetTap::removeIP(const InetAddress &ip)
return false ;
}
std : : set < InetAddress > WindowsEthernetTap : : ips ( ) const
std : : vector < InetAddress > WindowsEthernetTap : : ips ( ) const
{
static const InetAddress linkLocalLoopback ( " fe80::1 " , 64 ) ; // what is this and why does Windows assign it?
std : : set < InetAddress > addrs ;
std : : vector < InetAddress > addrs ;
if ( ! _initialized )
return addrs ;
@ -419,12 +444,12 @@ std::set<InetAddress> WindowsEthernetTap::ips() const
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 )
addrs . insert ( ip ) ;
addrs . push_back ( ip ) ;
} break ;
case AF_INET6 : {
InetAddress ip ( ipt - > Table [ i ] . Address . Ipv6 . sin6_addr . u . Byte , 16 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
if ( ( ip ! = linkLocalLoopback ) & & ( ip ! = InetAddress : : LO6 ) )
addrs . insert ( ip ) ;
addrs . push_back ( ip ) ;
} break ;
}
}
@ -433,6 +458,9 @@ std::set<InetAddress> WindowsEthernetTap::ips() const
}
} catch ( . . . ) { } // sanity check, shouldn't happen unless out of memory
std : : sort ( addrs . begin ( ) , addrs . end ( ) ) ;
std : : unique ( addrs . begin ( ) , addrs . end ( ) ) ;
return addrs ;
}
@ -472,23 +500,15 @@ void WindowsEthernetTap::setFriendlyName(const char *dn)
}
}
bool WindowsEthernetTap : : updateMulticastGroups ( std : : set < MulticastGroup > & groups )
void WindowsEthernetTap : : scanMulticastGroups ( std : : vector < MulticastGroup > & added , std : : vector < MulticastGroup > & removed )
{
if ( ! _initialized )
return false ;
return ;
HANDLE t = _tap ;
if ( t = = INVALID_HANDLE_VALUE )
return false ;
std : : set < MulticastGroup > newGroups ;
return ;
// Ensure that groups are added for each IP... this handles the MAC:ADI
// groups that are created from IPv4 addresses. Some of these may end
// up being duplicates of what the IOCTL returns but that's okay since
// the set<> will filter that.
std : : set < InetAddress > ipaddrs ( ips ( ) ) ;
for ( std : : set < InetAddress > : : const_iterator i ( ipaddrs . begin ( ) ) ; i ! = ipaddrs . end ( ) ; + + i )
newGroups . insert ( MulticastGroup : : deriveMulticastGroupForAddressResolution ( * i ) ) ;
std : : vector < MulticastGroup > newGroups ;
// 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
@ -503,32 +523,28 @@ bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
i + = 6 ;
if ( ( mac . isMulticast ( ) ) & & ( ! mac . isBroadcast ( ) ) ) {
// exclude the nulls that may be returned or any other junk Windows puts in there
newGroups . insert ( MulticastGroup ( mac , 0 ) ) ;
newGroups . push_back ( MulticastGroup ( mac , 0 ) ) ;
}
}
}
bool changed = false ;
std : : vector < InetAddress > allIps ( ips ( ) ) ;
for ( std : : vector < InetAddress > : : iterator ip ( allIps . begin ( ) ) ; ip ! = allIps . end ( ) ; + + ip )
newGroups . push_back ( MulticastGroup : : deriveMulticastGroupForAddressResolution ( * ip ) ) ;
for ( std : : set < MulticastGroup > : : iterator mg ( newGroups . begin ( ) ) ; mg ! = newGroups . end ( ) ; + + mg ) {
if ( ! groups . count ( * mg ) ) {
groups . insert ( * mg ) ;
changed = true ;
}
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 ) ;
}
for ( std : : set < MulticastGroup > : : iterator mg ( groups . begin ( ) ) ; mg ! = groups . end ( ) ; ) {
if ( ( ! newGroups . count ( * mg ) ) & & ( * mg ! = _blindWildcardMulticastGroup ) ) {
groups . erase ( mg + + ) ;
changed = true ;
} else + + mg ;
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 ) ;
}
return changed ;
}
bool WindowsEthernetTap : : injectPacketFromHost ( const MAC & from , const MAC & to , unsigned int etherType , const void * data , unsigned int len )
{
return false ;
_multicastGroups . swap ( newGroups ) ;
}
void WindowsEthernetTap : : threadMain ( )
@ -699,8 +715,8 @@ void WindowsEthernetTap::threadMain()
MAC from ( tapReadBuf + 6 , 6 ) ;
unsigned int etherType = ( ( ( ( unsigned int ) tapReadBuf [ 12 ] ) & 0xff ) < < 8 ) | ( ( ( unsigned int ) tapReadBuf [ 13 ] ) & 0xff ) ;
try {
Buffer < 4096 > tmp ( tapReadBuf + 14 , bytesRead - 14 ) ;
_handler ( _arg , from , to , etherType , tmp ) ;
// TODO: decode vlans
_handler ( _arg , _nwid , from , to , etherType , 0 , tapReadBuf + 14 , bytesRead - 14 ) ;
} catch ( . . . ) { } // handlers should not throw
}
}
@ -733,6 +749,71 @@ void WindowsEthernetTap::threadMain()
: : free ( tapReadBuf ) ;
}
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 )
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 ) ;
}
bool WindowsEthernetTap : : _disableTapDevice ( )
{
HANDLE devconLog = CreateFileA ( ( _pathToHelpers + " \\ devcon.log " ) . c_str ( ) , GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
@ -748,7 +829,7 @@ bool WindowsEthernetTap::_disableTapDevice()
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WindowsEthernetTapFactory : : W INENV . devcon + " \" disable @ " + _deviceInstanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WINENV . devcon + " \" disable @ " + _deviceInstanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
return false ;
@ -778,7 +859,7 @@ bool WindowsEthernetTap::_enableTapDevice()
PROCESS_INFORMATION processInfo ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WindowsEthernetTapFactory : : W INENV . devcon + " \" enable @ " + _deviceInstanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
if ( ! CreateProcessA ( NULL , ( LPSTR ) ( std : : string ( " \" " ) + _pathToHelpers + WINENV . devcon + " \" enable @ " + _deviceInstanceId ) . c_str ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & startupInfo , & processInfo ) ) {
if ( devconLog ! = INVALID_HANDLE_VALUE )
CloseHandle ( devconLog ) ;
return false ;