diff options
Diffstat (limited to 'osdep/WindowsEthernetTap.cpp')
-rw-r--r-- | osdep/WindowsEthernetTap.cpp | 193 |
1 files changed, 137 insertions, 56 deletions
diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index 682e33d7..d8fb3957 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -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::WINENV.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::WINENV.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,49 @@ 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()); +} + 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 +807,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::WINENV.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 +837,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::WINENV.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; @@ -863,4 +922,26 @@ void WindowsEthernetTap::_setRegistryIPv4Value(const char *regKey,const std::vec } } +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); +} + } // namespace ZeroTier |