diff --git a/main.cpp b/main.cpp index ef642281b..3eea745e7 100644 --- a/main.cpp +++ b/main.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include "windows/ZeroTierOne/ServiceInstaller.h" #include "windows/ZeroTierOne/ServiceBase.h" #include "windows/ZeroTierOne/ZeroTierOneService.h" @@ -422,7 +423,6 @@ static void _winPokeAHole() if ((ps > 0)&&(ps < (DWORD)sizeof(myPath))) { STARTUPINFOA startupInfo; PROCESS_INFORMATION processInfo; - fprintf(stderr,"*** path: %s\n",myPath); startupInfo.cb = sizeof(startupInfo); memset(&startupInfo,0,sizeof(STARTUPINFOA)); diff --git a/node/Constants.hpp b/node/Constants.hpp index 620f9b0d5..8a8c70f2f 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -387,12 +387,12 @@ /** * How often to broadcast beacons over physical local LANs */ -#define ZT_BEACON_INTERVAL ZT_PEER_DIRECT_PING_DELAY +#define ZT_BEACON_INTERVAL 30000 /** * Do not respond to any beacon more often than this */ -#define ZT_MIN_BEACON_RESPONSE_INTERVAL (ZT_BEACON_INTERVAL / 64) +#define ZT_MIN_BEACON_RESPONSE_INTERVAL (ZT_BEACON_INTERVAL / 32) /** * Minimum interval between attempts to do a software update diff --git a/node/EthernetTap.hpp b/node/EthernetTap.hpp index 0819fb49e..87ab607d0 100644 --- a/node/EthernetTap.hpp +++ b/node/EthernetTap.hpp @@ -196,21 +196,6 @@ public: */ virtual bool updateMulticastGroups(std::set &groups) = 0; - /** - * Should this tap device get a pseudo-default-route? - * - * Some platforms (cough Windows) want all "real" network devices to have a - * routing table entry for default, even if it's got a high metric and is - * never used and goes nowhere. If this returns true, the underlying node - * code will use RoutingTable to create one if no default route is - * otherwise defined. - * - * Base class default returns false. Override to return true if needed. - * - * @return True if pseudo-default-route should always exist - */ - virtual bool createPseudoDefaultRoute() const { return false; } - protected: const char *_implName; MAC _mac; diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 9ff5acea5..88ab6e490 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -39,6 +39,8 @@ namespace ZeroTier { const InetAddress InetAddress::LO4("127.0.0.1",0); const InetAddress InetAddress::LO6("::1",0); +const InetAddress InetAddress::DEFAULT4((uint32_t)0,0); +const InetAddress InetAddress::DEFAULT6((const void *)0,16,0); void InetAddress::set(const std::string &ip,unsigned int port) throw() @@ -63,11 +65,13 @@ void InetAddress::set(const void *ipBytes,unsigned int ipLen,unsigned int port) memset(&_sa,0,sizeof(_sa)); if (ipLen == 4) { setV4(); - memcpy(rawIpData(),ipBytes,4); + if (ipBytes) + memcpy(rawIpData(),ipBytes,4); setPort(port); } else if (ipLen == 16) { setV6(); - memcpy(rawIpData(),ipBytes,16); + if (ipBytes) + memcpy(rawIpData(),ipBytes,16); setPort(port); } } @@ -91,6 +95,16 @@ bool InetAddress::isLinkLocal() const return false; } +bool InetAddress::isDefaultRoute() const + throw() +{ + if (_sa.saddr.sa_family == AF_INET) + return ((_sa.sin.sin_addr.s_addr == 0)&&(_sa.sin.sin_port == 0)); + else if (_sa.saddr.sa_family == AF_INET6) + return ((Utils::isZero(_sa.sin6.sin6_addr.s6_addr,16))&&(_sa.sin6.sin6_port == 0)); + return false; +} + std::string InetAddress::toString() const { char buf[128],buf2[128]; diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 74e42670d..7cfb1abe0 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -77,6 +77,16 @@ public: */ static const InetAddress LO6; + /** + * 0.0.0.0/0 + */ + static const InetAddress DEFAULT4; + + /** + * ::/0 + */ + static const InetAddress DEFAULT6; + InetAddress() throw() { memset(&_sa,0,sizeof(_sa)); } InetAddress(const InetAddress &a) throw() { memcpy(&_sa,&a._sa,sizeof(_sa)); } InetAddress(const struct sockaddr *sa) throw() { this->set(sa); } @@ -147,6 +157,12 @@ public: bool isLinkLocal() const throw(); + /** + * @return True if this ip/netmask would represent a default route (e.g. 0.0.0.0/0) + */ + bool isDefaultRoute() const + throw(); + /** * @return True if this is a loopback address */ diff --git a/node/Network.cpp b/node/Network.cpp index c024a54b1..b38e96929 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -39,6 +39,7 @@ #include "Buffer.hpp" #include "EthernetTap.hpp" #include "EthernetTapFactory.hpp" +#include "RoutingTable.hpp" #define ZT_NETWORK_CERT_WRITE_BUF_SIZE 131072 diff --git a/osnet/WindowsEthernetTap.cpp b/osnet/WindowsEthernetTap.cpp index 0e580643e..f9acc6236 100644 --- a/osnet/WindowsEthernetTap.cpp +++ b/osnet/WindowsEthernetTap.cpp @@ -39,6 +39,11 @@ #include #include #include +#include +#include +#include + +#include #include "../node/Constants.hpp" @@ -52,38 +57,13 @@ // ff:ff:ff:ff:ff:ff with no ADI static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); +#define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE + namespace ZeroTier { // Only create or delete devices one at a time static Mutex _systemTapInitLock; -static void _syncIpsWithRegistry(const std::set &haveIps,const std::string netCfgInstanceId) -{ - // Update registry to contain all non-link-local IPs for this interface - std::string regMultiIps,regMultiNetmasks; - for(std::set::const_iterator i(haveIps.begin());i!=haveIps.end();++i) { - if (!i->isLinkLocal()) { - regMultiIps.append(i->toIpString()); - regMultiIps.push_back((char)0); - regMultiNetmasks.append(i->netmask().toIpString()); - regMultiNetmasks.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 (regMultiIps.length()) { - regMultiIps.push_back((char)0); - regMultiNetmasks.push_back((char)0); - RegSetKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"IPAddress",REG_MULTI_SZ,regMultiIps.data(),(DWORD)regMultiIps.length()); - RegSetKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"SubnetMask",REG_MULTI_SZ,regMultiNetmasks.data(),(DWORD)regMultiNetmasks.length()); - } else { - RegDeleteKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"IPAddress"); - RegDeleteKeyValueA(tcpIpInterfaces,netCfgInstanceId.c_str(),"SubnetMask"); - } - } - RegCloseKey(tcpIpInterfaces); -} - WindowsEthernetTap::WindowsEthernetTap( const char *pathToHelpers, const MAC &mac, @@ -97,6 +77,7 @@ WindowsEthernetTap::WindowsEthernetTap( EthernetTap("WindowsEthernetTap",mac,mtu,metric), _handler(handler), _arg(arg), + _nwid(nwid), _tap(INVALID_HANDLE_VALUE), _injectSemaphore(INVALID_HANDLE_VALUE), _pathToHelpers(pathToHelpers), @@ -171,7 +152,8 @@ WindowsEthernetTap::WindowsEthernetTap( } // If there is no device, try to create one - if (_netCfgInstanceId.length() == 0) { + bool creatingNewDevice = (_netCfgInstanceId.length() == 0); + if (creatingNewDevice) { // Log devcon output to a file 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) @@ -250,8 +232,17 @@ WindowsEthernetTap::WindowsEthernetTap( RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl); DWORD tmp = mtu; RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); + + //tmp = NDIS_DEVICE_TYPE_ENDPOINT; tmp = 0; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); // disable DHCP by default on new devices + 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)); + } RegCloseKey(nwAdapters); } else { RegCloseKey(nwAdapters); @@ -351,7 +342,15 @@ bool WindowsEthernetTap::addIP(const InetAddress &ip) } } - _syncIpsWithRegistry(haveIps,_netCfgInstanceId); + std::vector regIps(_getRegistryIPv4Value("IPAddress")); + if (std::find(regIps.begin(),regIps.end(),ip.toIpString()) == regIps.end()) { + std::vector regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); + regIps.push_back(ip.toIpString()); + regSubnetMasks.push_back(ip.netmask().toIpString()); + _setRegistryIPv4Value("IPAddress",regIps); + _setRegistryIPv4Value("SubnetMask",regSubnetMasks); + } + //_syncIpsWithRegistry(haveIps,_netCfgInstanceId); } catch ( ... ) { return false; } @@ -381,7 +380,20 @@ bool WindowsEthernetTap::removeIP(const InetAddress &ip) if (addr == ip) { DeleteUnicastIpAddressEntry(&(ipt->Table[i])); FreeMibTable(ipt); - _syncIpsWithRegistry(ips(),_netCfgInstanceId); + + std::vector regIps(_getRegistryIPv4Value("IPAddress")); + std::vector regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); + std::string ipstr(ip.toIpString()); + for(std::vector::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; + } + } + return true; } } @@ -516,11 +528,6 @@ bool WindowsEthernetTap::updateMulticastGroups(std::set &groups) return changed; } -bool WindowsEthernetTap::createPseudoDefaultRoute() const -{ - return true; -} - void WindowsEthernetTap::threadMain() throw() { @@ -540,10 +547,13 @@ void WindowsEthernetTap::threadMain() // Tap is in this weird Windows global pseudo file space Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); - // More insanity: repetatively try to enable/disable tap device. The first - // time we succeed, close it and do it again. This is to fix a driver init - // bug that seems to be extremely non-deterministic and to only occur after - // headless MSI upgrade. It cannot be reproduced in any other circumstance. + /* More insanity: repetatively try to enable/disable tap device. The first + * time we succeed, close it and do it again. This is to fix a driver init + * bug that seems to be extremely non-deterministic and to only occur after + * headless MSI upgrade. It cannot be reproduced in any other circumstance. + * + * Eventually when ZeroTier has actual money we will have someone create an + * NDIS6 tap driver. Yes, we'll likely be cool and open source it. */ bool throwOneAway = true; while (_run) { _disableTapDevice(); @@ -574,6 +584,118 @@ void WindowsEthernetTap::threadMain() } else break; } + /* code not currently used, but keep it around cause it was hard to figure out... + CoInitializeEx(NULL,COINIT_MULTITHREADED); + CComPtr nlm; + nlm.CoCreateInstance(CLSID_NetworkListManager); + if (nlm) { + for(int i=0;i<8;++i) { // wait up to 8s for the NLM (network awareness) to find and initialize its awareness of our new network + CComPtr nlmNets; + bool foundMyNet = false; + if (SUCCEEDED(nlm->GetNetworks(NLM_ENUM_NETWORK_ALL,&nlmNets))) { + DWORD dwReturn = 0; + while (!foundMyNet) { + CComPtr nlmNet; + HRESULT hr = nlmNets->Next(1,&nlmNet,&dwReturn); + if ((hr == S_OK)&&(dwReturn > 0)&&(nlmNet)) { + CComPtr nlmNetConns; + if (SUCCEEDED(nlmNet->GetNetworkConnections(&nlmNetConns))) { + for(;;) { + CComPtr nlmNetConn; + hr = nlmNetConns->Next(1,&nlmNetConn,&dwReturn); + if ((hr == S_OK)&&(dwReturn > 0)&&(nlmNetConn)) { + GUID netAdapterId; + nlmNetConn->GetAdapterId(&netAdapterId); + if (netAdapterId == _deviceGuid) { + foundMyNet = true; + printf("*** Found my net!\n"); + nlmNet->SetName(L"ZeroTier One Network"); + break; + } + } else break; + } + } + } else break; + } + } + if (foundMyNet) + break; + else Thread::sleep(1000); + } + } + */ + +#ifdef ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE + /* 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. + * + * But these networks don't usually have default routes, so what + * do we do? Answer: add a fake one that's never used and goes + * nowhere. But it's got to resolve to an ARP address. So why + * don't we just make up one of those too?!? Shove it in there + * as a permanent statuc ARP entry and now Windows will think it + * has a real live default route at our bogus IP. + * + * 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. + * + * But yes, this works, and it makes our networks look and behave + * the way they should. + * + * Like Jesse Pinkman would say: "YEEEEAAH BITCH!" */ + for(int i=0;i<8;++i) { // also wait up to 8s for this, though if we got the NLM part we're probably okay + 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 = 0x01010101; // 1.1.1.1 + nr.Metric = 9999; // do not use as real default route + nr.Protocol = MIB_IPPROTO_NETMGMT; + DWORD result = CreateIpForwardEntry2(&nr); + if (result == NO_ERROR) { + MIB_IPNET_ROW2 ipnr; + memset(&ipnr,0,sizeof(ipnr)); + ipnr.Address.si_family = AF_INET; + ipnr.Address.Ipv4.sin_addr.s_addr = 0x01010101; + 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; + CreateIpNetEntry2(&ipnr); + break; // stop retrying, we're done + } else Thread::sleep(1000); + } +#endif + memset(&tapOvlRead,0,sizeof(tapOvlRead)); tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); memset(&tapOvlWrite,0,sizeof(tapOvlWrite)); @@ -589,7 +711,7 @@ void WindowsEthernetTap::threadMain() for(;;) { if (!_run) break; - DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,5000,TRUE); + DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,10000,TRUE); if (!_run) break; if ((r == WAIT_TIMEOUT)||(r == WAIT_FAILED)) @@ -717,4 +839,54 @@ NET_IFINDEX WindowsEthernetTap::_getDeviceIndex() throw std::runtime_error("interface not found"); } +std::vector WindowsEthernetTap::_getRegistryIPv4Value(const char *regKey) +{ + std::vector 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 &value) +{ + std::string regMulti; + for(std::vector::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); + } +} + } // namespace ZeroTier diff --git a/osnet/WindowsEthernetTap.hpp b/osnet/WindowsEthernetTap.hpp index 71ec2ce2f..e5f5e4b26 100644 --- a/osnet/WindowsEthernetTap.hpp +++ b/osnet/WindowsEthernetTap.hpp @@ -71,7 +71,6 @@ public: virtual std::string deviceName() const; virtual void setFriendlyName(const char *friendlyName); virtual bool updateMulticastGroups(std::set &groups); - virtual bool createPseudoDefaultRoute() const; inline const NET_LUID &luid() const { return _deviceLuid; } inline const GUID &guid() const { return _deviceGuid; } @@ -84,9 +83,12 @@ private: bool _disableTapDevice(); bool _enableTapDevice(); NET_IFINDEX _getDeviceIndex(); // throws on failure + std::vector _getRegistryIPv4Value(const char *regKey); + void _setRegistryIPv4Value(const char *regKey,const std::vector &value); void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); void *_arg; + uint64_t _nwid; Thread _thread; volatile HANDLE _tap; diff --git a/osnet/WindowsRoutingTable.cpp b/osnet/WindowsRoutingTable.cpp index aa215fb06..eef03ae65 100644 --- a/osnet/WindowsRoutingTable.cpp +++ b/osnet/WindowsRoutingTable.cpp @@ -151,7 +151,9 @@ RoutingTable::Entry WindowsRoutingTable::set(const InetAddress &destination,cons _copyInetAddressToSockaddrInet(gateway,nr.NextHop); nr.Metric = metric; nr.Protocol = MIB_IPPROTO_NETMGMT; - CreateIpForwardEntry2(&nr); + DWORD result = CreateIpForwardEntry2(&nr); + if (result != NO_ERROR) + return RoutingTable::Entry(); } std::vector rtab(get(true,true));