diff --git a/CMakeLists.txt b/CMakeLists.txt index 98021e89c..1a2102da8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,9 +246,8 @@ if(NOT PACKAGE_STATIC) endif(BUILD_CENTRAL_CONTROLLER) add_subdirectory(core) - add_subdirectory(controller) +# add_subdirectory(controller) add_subdirectory(osdep) - #add_subdirectory(serviceiocore) #file(GLOB go_src # ${CMAKE_SOURCE_DIR}/cmd/*.go diff --git a/osdep/BSDEthernetTap.cpp b/osdep/BSDEthernetTap.cpp deleted file mode 100644 index 9307d6445..000000000 --- a/osdep/BSDEthernetTap.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../core/Constants.hpp" -#include "../core/Utils.hpp" -#include "../core/Mutex.hpp" -#include "OSUtils.hpp" -#include "BSDEthernetTap.hpp" - -#define ZT_BASE32_CHARS "0123456789abcdefghijklmnopqrstuv" - -// ff:ff:ff:ff:ff:ff with no ADI -static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); - -namespace ZeroTier { - -BSDEthernetTap::BSDEthernetTap( - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg) : - _handler(handler), - _arg(arg), - _nwid(nwid), - _mtu(mtu), - _metric(metric), - _fd(0), - _enabled(true) -{ - static Mutex globalTapCreateLock; - char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32]; - - Mutex::Lock _gl(globalTapCreateLock); - -#ifdef __FreeBSD__ - /* FreeBSD allows long interface names and interface renaming */ - - _dev = "zt"; - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 50) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 45) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 40) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 35) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 30) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 25) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 20) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 15) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 10) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]); - - std::vector devFiles(OSUtils::listDirectory("/dev")); - for(int i=9993;i<(9993+128);++i) { - OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); - OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); - if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) { - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"create",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } else throw std::runtime_error("fork() failed"); - - struct stat stattmp; - if (!stat(devpath,&stattmp)) { - cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"name",_dev.c_str(),(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) - throw std::runtime_error("ifconfig rename operation failed"); - } else throw std::runtime_error("fork() failed"); - - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) - break; - else throw std::runtime_error("unable to open created tap device"); - } else { - throw std::runtime_error("cannot find /dev node for newly created tap device"); - } - } - } -#else - /* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */ - - for(int i=0;i<64;++i) { - OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); - OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) { - _dev = tmpdevname; - break; - } - } -#endif - - if (_fd <= 0) - throw std::runtime_error("unable to open TAP device or no more devices available"); - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - // Configure MAC address and MTU, bring interface up - OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); - OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu); - OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric); - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) { - ::close(_fd); - throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); - } - } - - // Set close-on-exec so that devices cannot persist if we fork/exec for update - fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); - - ::pipe(_shutdownSignalPipe); - - _thread = Thread::start(this); -} - -BSDEthernetTap::~BSDEthernetTap() -{ - ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - Thread::join(_thread); - ::close(_fd); - ::close(_shutdownSignalPipe[0]); - ::close(_shutdownSignalPipe[1]); - - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } -} - -void BSDEthernetTap::setEnabled(bool en) -{ - _enabled = en; -} - -bool BSDEthernetTap::enabled() const -{ - return _enabled; -} - -static bool ___removeIp(const std::string &_dev,const InetAddress &ip) -{ - long cpid = (long)vfork(); - if (cpid == 0) { - char ipbuf[64]; - execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString(ipbuf),"-alias",(const char *)0); - _exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; // never reached, make compiler shut up about return value -} - -bool BSDEthernetTap::addIp(const InetAddress &ip) -{ - if (!ip) - return false; - - std::vector allIps(ips()); - if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) - return true; // IP/netmask already assigned - - // Remove and reconfigure if address is the same but netmask is different - for(std::vector::iterator i(allIps.begin());i!=allIps.end();++i) { - if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { - if (___removeIp(_dev,*i)) - break; - } - } - - long cpid = (long)vfork(); - if (cpid == 0) { - char tmp[128]; - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString(tmp),"alias",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; -} - -bool BSDEthernetTap::removeIp(const InetAddress &ip) -{ - if (!ip) - return false; - std::vector allIps(ips()); - if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) { - if (___removeIp(_dev,ip)) - return true; - } - return false; -} - -std::vector BSDEthernetTap::ips() const -{ - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (getifaddrs(&ifa)) - return std::vector(); - - std::vector r; - - struct ifaddrs *p = ifa; - while (p) { - if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { - switch(p->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; - struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; - r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); - } break; - case AF_INET6: { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; - struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; - uint32_t b[4]; - memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); - r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); - } break; - } - } - p = p->ifa_next; - } - - if (ifa) - freeifaddrs(ifa); - - std::sort(r.begin(),r.end()); - std::unique(r.begin(),r.end()); - - return r; -} - -void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - uint8_t putBuf[ZT_MAX_MTU + 64]; - if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { - to.copyTo(putBuf); - from.copyTo(putBuf + 6); - Utils::storeBigEndian(putBuf + 12, (uint16_t)etherType); - Utils::copy(putBuf + 14,data,len); - len += 14; - ::write(_fd,putBuf,len); - } -} - -std::string BSDEthernetTap::deviceName() const -{ - return _dev; -} - -void BSDEthernetTap::setFriendlyName(const char *friendlyName) -{ -} - -void BSDEthernetTap::scanMulticastGroups(std::vector &added,std::vector &removed) -{ - std::vector newGroups; - -#ifndef __OpenBSD__ - struct ifmaddrs *ifmap = (struct ifmaddrs *)0; - if (!getifmaddrs(&ifmap)) { - struct ifmaddrs *p = ifmap; - while (p) { - if (p->ifma_addr->sa_family == AF_LINK) { - struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name; - struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; - if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen))) - newGroups.push_back(MulticastGroup(MAC((const uint8_t *)(la->sdl_data + la->sdl_nlen)),0)); - } - p = p->ifma_next; - } - freeifmaddrs(ifmap); - } -#endif // __OpenBSD__ - - std::vector allIps(ips()); - for(std::vector::iterator ip(allIps.begin());ip!=allIps.end();++ip) - newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); - - std::sort(newGroups.begin(),newGroups.end()); - std::unique(newGroups.begin(),newGroups.end()); - - for(std::vector::iterator m(newGroups.begin());m!=newGroups.end();++m) { - if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) - added.push_back(*m); - } - for(std::vector::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { - if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) - removed.push_back(*m); - } - - _multicastGroups.swap(newGroups); -} - -void BSDEthernetTap::setMtu(unsigned int mtu) -{ - if (mtu != _mtu) { - _mtu = mtu; - long cpid = (long)vfork(); - if (cpid == 0) { - char tmp[64]; - OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu); - execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0); - _exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - } - } -} - -void BSDEthernetTap::threadMain() - throw() -{ - fd_set readfds,nullfds; - MAC to,from; - int n,nfds,r; - uint8_t getBuf[ZT_MAX_MTU + 64]; - - // Wait for a moment after startup -- wait for Network to finish - // constructing itself. - Thread::sleep(500); - - FD_ZERO(&readfds); - FD_ZERO(&nullfds); - nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - - r = 0; - for(;;) { - FD_SET(_shutdownSignalPipe[0],&readfds); - FD_SET(_fd,&readfds); - select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - - if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread - break; - - if (FD_ISSET(_fd,&readfds)) { - n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); - if (n < 0) { - if ((errno != EINTR)&&(errno != ETIMEDOUT)) - break; - } else { - // Some tap drivers like to send the ethernet frame and the - // payload in two chunks, so handle that by accumulating - // data until we have at least a frame. - r += n; - if (r > 14) { - if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms - r = _mtu + 14; - - if (_enabled) { - to.setTo(getBuf); - from.setTo(getBuf + 6); - _handler(_arg,(void *)0,_nwid,from,to,Utils::loadBigEndian(getBuf + 12),0,(const void *)(getBuf + 14),r - 14); - } - - r = 0; - } - } - } - } -} - -} // namespace ZeroTier diff --git a/osdep/BSDEthernetTap.hpp b/osdep/BSDEthernetTap.hpp deleted file mode 100644 index 179f0fd40..000000000 --- a/osdep/BSDEthernetTap.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_BSDETHERNETTAP_HPP -#define ZT_BSDETHERNETTAP_HPP - -#include -#include - -#include -#include -#include - -#include "../core/Constants.hpp" -#include "../core/MulticastGroup.hpp" -#include "../core/MAC.hpp" -#include "Thread.hpp" -#include "EthernetTap.hpp" - -namespace ZeroTier { - -class BSDEthernetTap : public EthernetTap -{ -public: - BSDEthernetTap( - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg); - - virtual ~BSDEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIp(const InetAddress &ip); - virtual bool removeIp(const InetAddress &ip); - virtual std::vector ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual void scanMulticastGroups(std::vector &added,std::vector &removed); - virtual void setMtu(unsigned int mtu); - - void threadMain() - throw(); - -private: - void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); - void *_arg; - uint64_t _nwid; - Thread _thread; - std::string _dev; - std::vector _multicastGroups; - unsigned int _mtu; - unsigned int _metric; - int _fd; - int _shutdownSignalPipe[2]; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif diff --git a/osdep/CMakeLists.txt b/osdep/CMakeLists.txt index 55d0283d9..39f47e094 100644 --- a/osdep/CMakeLists.txt +++ b/osdep/CMakeLists.txt @@ -2,32 +2,32 @@ cmake_minimum_required(VERSION 3.0) project(zt_osdep) set(src - ManagedRoute.cpp +# ManagedRoute.cpp OSUtils.cpp rust-osdep.cpp ) set(headers - ManagedRoute.hpp +# ManagedRoute.hpp OSUtils.hpp Thread.hpp rust-osdep.h ) -if(WIN32) - set(src ${src} WindowsEthernetTap.cpp) - set(headers ${headers} WindowsEthernetTap.hpp) -elseif(UNIX) - if(CMAKE_SYSTEM_NAME MATCHES "BSD") - set(src ${src} BSDEthernetTap.cpp) - set(headers ${headers} BSDEthernetTap.hpp) - endif() - - if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - set(src ${src} freebsd_getifmaddrs.c) - set(headers ${headers} freebsd_getifmaddrs.h) - endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") -endif(WIN32) +#if(WIN32) +# set(src ${src} WindowsEthernetTap.cpp) +# set(headers ${headers} WindowsEthernetTap.hpp) +#elseif(UNIX) +# if(CMAKE_SYSTEM_NAME MATCHES "BSD") +# set(src ${src} BSDEthernetTap.cpp) +# set(headers ${headers} BSDEthernetTap.hpp) +# endif() +# +# if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") +# set(src ${src} freebsd_getifmaddrs.c) +# set(headers ${headers} freebsd_getifmaddrs.h) +# endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") +#endif(WIN32) add_library(${PROJECT_NAME} STATIC ${src} ${headers}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) diff --git a/osdep/EthernetTap.cpp b/osdep/EthernetTap.cpp deleted file mode 100644 index 5535b14d8..000000000 --- a/osdep/EthernetTap.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "EthernetTap.hpp" -#include "OSUtils.hpp" - -#ifdef ZT_SDK - -#include "../controller/EmbeddedNetworkController.hpp" -#include "../core/Node.hpp" -#include "../include/VirtualTap.hpp" - -#else - -#ifdef __APPLE__ -#include -#include "MacEthernetTap.hpp" -#include "MacKextEthernetTap.hpp" -#endif // __APPLE__ - -#ifdef __LINUX__ -#include "LinuxEthernetTap.hpp" -#endif // __LINUX__ - -#ifdef __WINDOWS__ -#include "WindowsEthernetTap.hpp" -#endif // __WINDOWS__ - -#ifdef __FreeBSD__ -#include "BSDEthernetTap.hpp" -#endif // __FreeBSD__ - -#ifdef __NetBSD__ -#include "NetBSDEthernetTap.hpp" -#endif // __NetBSD__ - -#ifdef __OpenBSD__ -#include "BSDEthernetTap.hpp" -#endif // __OpenBSD__ - -#endif - -namespace ZeroTier { - -std::shared_ptr EthernetTap::newInstance( - const char *tapDeviceType, // OS-specific, NULL for default - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg) -{ - -#ifdef ZT_SDK - - return std::shared_ptr(new VirtualTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); - -#else // not ZT_SDK - -#ifdef __APPLE__ - char osrelease[256]; - size_t size = sizeof(osrelease); - if (sysctlbyname("kern.osrelease",osrelease,&size,nullptr,0) == 0) { - char *dotAt = strchr(osrelease,'.'); - if (dotAt) { - *dotAt = (char)0; - // The "feth" virtual Ethernet device type appeared in Darwin 17.x.x. Older versions - // (Sierra and earlier) must use the a kernel extension. - if (strtol(osrelease,(char **)0,10) < 17) { - return std::shared_ptr(new MacKextEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); - } else { - return std::shared_ptr(new MacEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); - } - } - } -#endif // __APPLE__ - -#ifdef __LINUX__ - return std::shared_ptr(new LinuxEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); -#endif // __LINUX__ - -#ifdef __WINDOWS__ - return std::shared_ptr(new WindowsEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); -#endif // __WINDOWS__ - -#ifdef __FreeBSD__ - return std::shared_ptr(new BSDEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); -#endif // __FreeBSD__ - -#ifdef __NetBSD__ - return std::shared_ptr(new NetBSDEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); -#endif // __NetBSD__ - -#ifdef __OpenBSD__ - return std::shared_ptr(new BSDEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); -#endif // __OpenBSD__ - -#endif // ZT_SDK? - - return std::shared_ptr(); -} - -EthernetTap::EthernetTap() {} -EthernetTap::~EthernetTap() {} - -bool EthernetTap::addIps(std::vector ips) -{ - for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) { - if (!addIp(*i)) - return false; - } - return true; -} - -std::string EthernetTap::routingDeviceName() const -{ -#ifdef __WINDOWS__ - char tapdev[64]; - OSUtils::ztsnprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)(((const WindowsEthernetTap *)(this))->luid().Value)); - return std::string(tapdev); -#else - return this->deviceName(); -#endif -} - -} // namespace ZeroTier diff --git a/osdep/EthernetTap.hpp b/osdep/EthernetTap.hpp deleted file mode 100644 index 6066b5be1..000000000 --- a/osdep/EthernetTap.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_ETHERNETTAP_HPP -#define ZT_ETHERNETTAP_HPP - -#include "../core/Constants.hpp" -#include "../core/MAC.hpp" -#include "../core/InetAddress.hpp" -#include "../core/MulticastGroup.hpp" - -#include -#include -#include -#include -#include - -namespace ZeroTier { - -class EthernetTap -{ -public: - static std::shared_ptr newInstance( - const char *tapDeviceType, // OS-specific, NULL for default - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg); - - EthernetTap(); - virtual ~EthernetTap(); - - virtual void setEnabled(bool en) = 0; - virtual bool enabled() const = 0; - virtual bool addIp(const InetAddress &ip) = 0; - virtual bool addIps(std::vector ips); // uses addIp() unless overridden - virtual bool removeIp(const InetAddress &ip) = 0; - virtual std::vector ips() const = 0; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) = 0; - virtual std::string deviceName() const = 0; - virtual std::string routingDeviceName() const; - virtual void setFriendlyName(const char *friendlyName) = 0; - virtual void scanMulticastGroups(std::vector &added,std::vector &removed) = 0; - virtual void setMtu(unsigned int mtu) = 0; -}; - -} // namespace ZeroTier - -#endif diff --git a/osdep/LinuxNetLink.cpp b/osdep/LinuxNetLink.cpp deleted file mode 100644 index 421da1f63..000000000 --- a/osdep/LinuxNetLink.cpp +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "../node/Constants.hpp" - -//#define ZT_NETLINK_TRACE - -#ifdef __LINUX__ - -#include "LinuxNetLink.hpp" - -#include -#include - -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif - -namespace ZeroTier { - -struct nl_route_req { - struct nlmsghdr nl; - struct rtmsg rt; - char buf[8192]; -}; - -struct nl_if_req { - struct nlmsghdr nl; - struct ifinfomsg ifa; - char buf[8192]; -}; - -struct nl_adr_req { - struct nlmsghdr nl; - struct ifaddrmsg ifa; - char buf[8192]; -}; - -LinuxNetLink::LinuxNetLink() - : _t() - , _running(false) - , _seq(0) - , _interfaces() - , _if_m() - , _fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) - , _la({0}) -{ - // set socket timeout to 1 sec so we're not permablocking recv() calls - _setSocketTimeout(_fd, 1); - - _la.nl_family = AF_NETLINK; - _la.nl_pid = 0; //getpid()+1; - _la.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR|RTMGRP_IPV4_ROUTE|RTMGRP_IPV6_ROUTE|RTMGRP_NOTIFY; - if (bind(_fd, (struct sockaddr*)&_la, sizeof(_la))) { - fprintf(stderr, "Error connecting to RTNETLINK: %s\n", strerror(errno)); - ::exit(1); - } - - _requestIPv4Routes(); - _requestIPv6Routes(); - _requestInterfaceList(); - - _running = true; - _t = Thread::start(this); -} - -LinuxNetLink::~LinuxNetLink() -{ - _running = false; - Thread::join(_t); - ::close(_fd); -} - -void LinuxNetLink::_setSocketTimeout(int fd, int seconds) -{ - struct timeval tv; - tv.tv_sec = seconds; - tv.tv_usec = 0; - if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) != 0) { -#ifdef ZT_NETLINK_TRACE - fprintf(stderr, "setsockopt failed: %s\n", strerror(errno)); -#endif - } -} - -#define ZT_NL_BUF_SIZE 16384 -int LinuxNetLink::_doRecv(int fd) -{ - char *buf = nullptr; - if (posix_memalign((void **)&buf,16,ZT_NL_BUF_SIZE) != 0) { - fprintf(stderr,"malloc failed!\n"); - ::exit(1); - } - if (!buf) { - fprintf(stderr,"malloc failed!\n"); - ::exit(1); - } - - char *p = NULL; - struct nlmsghdr *nlp; - int nll = 0; - int rtn = 0; - p = buf; - - for(;;) { - rtn = recv(fd, p, ZT_NL_BUF_SIZE - nll, 0); - - if (rtn > 0) { - nlp = (struct nlmsghdr *)p; - - if(nlp->nlmsg_type == NLMSG_ERROR && (nlp->nlmsg_flags & NLM_F_ACK) != NLM_F_ACK) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nlp); - if (err->error != 0) { -#ifdef ZT_NETLINK_TRACE - fprintf(stderr, "rtnetlink error: %s\n", strerror(-(err->error))); -#endif - } - p = buf; - nll = 0; - break; - } - - if (nlp->nlmsg_type == NLMSG_NOOP) { - break; - } - - if( (nlp->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI || (nlp->nlmsg_type == NLMSG_DONE)) - { - if (nlp->nlmsg_type == NLMSG_DONE) { - _processMessage(nlp, nll); - p = buf; - nll = 0; - break; - } - p += rtn; - nll += rtn; - } - - if (nlp->nlmsg_type == NLMSG_OVERRUN) { -#ifdef ZT_NETLINK_TRACE - fprintf(stderr, "NLMSG_OVERRUN: Data lost\n"); -#endif - p = buf; - nll = 0; - break; - } - - nll += rtn; - - _processMessage(nlp, nll); - - p = buf; - nll = 0; - break; - } else { - break; - } - } - - free(buf); - - return rtn; -} - -void LinuxNetLink::threadMain() throw() -{ - int rtn = 0; - while(_running) { - rtn = _doRecv(_fd); - if (rtn <= 0) { - Thread::sleep(250); - continue; - } - } -} - -void LinuxNetLink::_processMessage(struct nlmsghdr *nlp, int nll) -{ - for(; NLMSG_OK(nlp, nll); nlp=NLMSG_NEXT(nlp, nll)) - { - switch(nlp->nlmsg_type) - { - case RTM_NEWLINK: - _linkAdded(nlp); - break; - case RTM_DELLINK: - _linkDeleted(nlp); - break; - case RTM_NEWADDR: - _ipAddressAdded(nlp); - break; - case RTM_DELADDR: - _ipAddressDeleted(nlp); - break; - case RTM_NEWROUTE: - _routeAdded(nlp); - break; - case RTM_DELROUTE: - _routeDeleted(nlp); - break; - default: - break; - } - } -} - -void LinuxNetLink::_ipAddressAdded(struct nlmsghdr *nlp) -{ -#ifdef ZT_NETLINK_TRACE - struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap); - int ifal = IFA_PAYLOAD(nlp); - - char addr[40] = {0}; - char local[40] = {0}; - char label[40] = {0}; - char bcast[40] = {0}; - - for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal)) - { - switch(rtap->rta_type) { - case IFA_ADDRESS: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40); - break; - case IFA_LOCAL: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40); - break; - case IFA_LABEL: - memcpy(label, RTA_DATA(rtap), 40); - break; - case IFA_BROADCAST: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40); - break; - } - } - - fprintf(stderr,"Added IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); -#endif -} - -void LinuxNetLink::_ipAddressDeleted(struct nlmsghdr *nlp) -{ -#ifdef ZT_NETLINK_TRACE - struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap); - int ifal = IFA_PAYLOAD(nlp); - - char addr[40] = {0}; - char local[40] = {0}; - char label[40] = {0}; - char bcast[40] = {0}; - - for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal)) - { - switch(rtap->rta_type) { - case IFA_ADDRESS: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40); - break; - case IFA_LOCAL: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40); - break; - case IFA_LABEL: - memcpy(label, RTA_DATA(rtap), 40); - break; - case IFA_BROADCAST: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40); - break; - } - } - - fprintf(stderr, "Removed IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); -#endif -} - -void LinuxNetLink::_routeAdded(struct nlmsghdr *nlp) -{ - char dsts[40] = {0}; - char gws[40] = {0}; - char srcs[40] = {0}; - char ifs[16] = {0}; - char ms[24] = {0}; - - struct rtmsg *rtp = (struct rtmsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp); - int rtl = RTM_PAYLOAD(nlp); - - Route r; - bool wecare = false; - - for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl)) - { - switch(rtap->rta_type) - { - case RTA_DST: - switch(rtp->rtm_family) { - case AF_INET: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); - r.target.set(RTA_DATA(rtap), 4, 0); - wecare = true; - break; - case AF_INET6: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); - r.target.set(RTA_DATA(rtap), 16, 0); - wecare = true; - break; - } - break; - case RTA_SRC: - switch(rtp->rtm_family) { - case AF_INET: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); - r.src.set(RTA_DATA(rtap), 4, 0); - wecare = true; - break; - case AF_INET6: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); - r.src.set(RTA_DATA(rtap), 16, 0); - wecare = true; - break; - } - break; - case RTA_GATEWAY: - switch(rtp->rtm_family) { - case AF_INET: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); - r.via.set(RTA_DATA(rtap), 4, 0); - wecare = true; - break; - case AF_INET6: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); - r.via.set(RTA_DATA(rtap), 16, 0); - wecare = true; - break; - } - break; - case RTA_OIF: - switch(rtp->rtm_family) { - case AF_INET: - r.ifidx = *((int*)RTA_DATA(rtap)); - wecare = true; - break; - case AF_INET6: - r.ifidx = *((int*)RTA_DATA(rtap)); - wecare = true; - break; - } - sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); - break; - } - } - - if (wecare) { - Mutex::Lock rl(_routes_m); - _routes[r.target].insert(r); - } - -#ifdef ZT_NETLINK_TRACE - sprintf(ms, "%d", rtp->rtm_dst_len); - fprintf(stderr, "Route Added: dst %s/%s gw %s src %s if %s\n", dsts, ms, gws, srcs, ifs); -#endif -} - -void LinuxNetLink::_routeDeleted(struct nlmsghdr *nlp) -{ - char dsts[40] = {0}; - char gws[40] = {0}; - char srcs[40] = {0}; - char ifs[16] = {0}; - char ms[24] = {0}; - - struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp); - int rtl = RTM_PAYLOAD(nlp); - - Route r; - bool wecare = false; - - for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl)) - { - switch(rtap->rta_type) - { - case RTA_DST: - switch(rtp->rtm_family) { - case AF_INET: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); - r.target.set(RTA_DATA(rtap), 4, 0); - wecare = true; - break; - case AF_INET6: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); - r.target.set(RTA_DATA(rtap), 16, 0); - wecare = true; - break; - } - break; - case RTA_SRC: - switch(rtp->rtm_family) { - case AF_INET: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); - r.src.set(RTA_DATA(rtap), 4, 0); - wecare = true; - break; - case AF_INET6: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); - r.src.set(RTA_DATA(rtap), 16, 0); - wecare = true; - break; - } - break; - case RTA_GATEWAY: - switch(rtp->rtm_family) { - case AF_INET: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); - r.via.set(RTA_DATA(rtap), 4, 0); - wecare = true; - break; - case AF_INET6: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); - r.via.set(RTA_DATA(rtap), 16, 0); - wecare = true; - break; - } - break; - case RTA_OIF: - switch(rtp->rtm_family) { - case AF_INET: - r.ifidx = *((int*)RTA_DATA(rtap)); - wecare = true; - break; - case AF_INET6: - r.ifidx = *((int*)RTA_DATA(rtap)); - wecare = true; - break; - } - sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); - break; - } - } - - if (wecare) { - Mutex::Lock rl(_routes_m); - _routes[r.target].erase(r); - } - -#ifdef ZT_NETLINK_TRACE - sprintf(ms, "%d", rtp->rtm_dst_len); - fprintf(stderr, "Route Deleted: dst %s/%s gw %s src %s if %s\n", dsts, ms, gws, srcs, ifs); -#endif -} - -void LinuxNetLink::_linkAdded(struct nlmsghdr *nlp) -{ - unsigned char mac_bin[6] = {0}; - unsigned int mtu = 0; - char ifname[IFNAMSIZ] = {0}; - - struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip); - int ifil = RTM_PAYLOAD(nlp); - - const char *ptr = (const char *)0; - for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil)) - { - switch(rtap->rta_type) { - case IFLA_ADDRESS: - ptr = (const char *)RTA_DATA(rtap); - memcpy(mac_bin, ptr, 6); - break; - case IFLA_IFNAME: - ptr = (const char *)RTA_DATA(rtap); - memcpy(ifname, ptr, strlen(ptr)); - break; - case IFLA_MTU: - memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int)); - break; - } - } - - { - Mutex::Lock l(_if_m); - struct iface_entry &entry = _interfaces[ifip->ifi_index]; - entry.index = ifip->ifi_index; - memcpy(entry.ifacename, ifname, sizeof(ifname)); - snprintf(entry.mac,sizeof(entry.mac),"%.02x:%.02x:%.02x:%.02x:%.02x:%.02x",(unsigned int)mac_bin[0],(unsigned int)mac_bin[1],(unsigned int)mac_bin[2],(unsigned int)mac_bin[3],(unsigned int)mac_bin[4],(unsigned int)mac_bin[5]); - memcpy(entry.mac_bin, mac_bin, 6); - entry.mtu = mtu; - } -} - -void LinuxNetLink::_linkDeleted(struct nlmsghdr *nlp) -{ - unsigned int mtu = 0; - char ifname[40] = {0}; - - struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip); - int ifil = RTM_PAYLOAD(nlp); - - const char *ptr = (const char *)0; - for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil)) - { - switch(rtap->rta_type) { - case IFLA_IFNAME: - ptr = (const char*)RTA_DATA(rtap); - memcpy(ifname, ptr, strlen(ptr)); - break; - case IFLA_MTU: - memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int)); - break; - } - } - - { - Mutex::Lock l(_if_m); - if(_interfaces.contains(ifip->ifi_index)) { - _interfaces.erase(ifip->ifi_index); - } - } -} - -void LinuxNetLink::_requestIPv4Routes() -{ - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - la.nl_groups = RTMGRP_IPV4_ROUTE; - if(bind(fd, (struct sockaddr*)&la, sizeof(la))) { - fprintf(stderr, "Error binding RTNETLINK (_requiestIPv4Routes #1): %s\n", strerror(errno)); - close(fd); - return; - } - - struct nl_route_req req; - bzero(&req, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_type = RTM_GETROUTE; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.rt.rtm_family = AF_INET; - req.rt.rtm_table = RT_TABLE_MAIN; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -void LinuxNetLink::_requestIPv6Routes() -{ - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - la.nl_groups = RTMGRP_IPV6_ROUTE; - if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { - fprintf(stderr, "Error binding RTNETLINK (_requestIPv6Routes #1): %s\n", strerror(errno)); - close(fd); - return; - } - - struct nl_route_req req; - bzero(&req, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_type = RTM_GETROUTE; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.rt.rtm_family = AF_INET6; - req.rt.rtm_table = RT_TABLE_MAIN; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -void LinuxNetLink::_requestInterfaceList() -{ - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - la.nl_groups = RTMGRP_LINK; - if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { - fprintf(stderr, "Error binding RTNETLINK (_requestInterfaceList #1): %s\n", strerror(errno)); - close(fd); - return; - } - - struct nl_if_req req; - bzero(&req, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_type = RTM_GETLINK; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.ifa.ifi_family = AF_UNSPEC; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -void LinuxNetLink::addRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName) -{ - if (!target) return; - - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - bzero(&la, sizeof(la)); - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - - if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { - fprintf(stderr, "Error binding RTNETLINK (addRoute #1): %s\n", strerror(errno)); - close(fd); - return; - } - -#ifdef ZT_NETLINK_TRACE - char tmp[64]; - char tmp2[64]; - char tmp3[64]; - fprintf(stderr, "Adding Route. target: %s via: %s src: %s iface: %s\n", target.toString(tmp), via.toString(tmp2), src.toString(tmp3), ifaceName); -#endif - - int rtl = sizeof(struct rtmsg); - struct nl_route_req req; - bzero(&req, sizeof(req)); - - struct rtattr *rtap = (struct rtattr *)req.buf; - rtap->rta_type = RTA_DST; - if (target.isV4()) { - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr)); - } else { - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr)); - } - rtl += rtap->rta_len; - - if(via) { - rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len); - rtap->rta_type = RTA_GATEWAY; - if(via.isV4()) { - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr)); - } else { - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr)); - } - rtl += rtap->rta_len; - } else if (src) { - rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len); - rtap->rta_type = RTA_SRC; - if(src.isV4()) { - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&src)->sin_addr, sizeof(struct in_addr)); - - } else { - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&src)->sin6_addr, sizeof(struct in6_addr)); - } - req.rt.rtm_src_len = src.netmaskBits(); - } - - if (ifaceName != NULL) { - int interface_index = _indexForInterface(ifaceName); - if (interface_index != -1) { - rtap = (struct rtattr *) (((char*)rtap) + rtap->rta_len); - rtap->rta_type = RTA_OIF; - rtap->rta_len = RTA_LENGTH(sizeof(int)); - memcpy(RTA_DATA(rtap), &interface_index, sizeof(int)); - rtl += rtap->rta_len; - } - } - - req.nl.nlmsg_len = NLMSG_LENGTH(rtl); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE | NLM_F_ACK; - req.nl.nlmsg_type = RTM_NEWROUTE; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.rt.rtm_family = target.ss_family; - req.rt.rtm_table = RT_TABLE_MAIN; - req.rt.rtm_protocol = RTPROT_STATIC; - req.rt.rtm_scope = RT_SCOPE_UNIVERSE; - req.rt.rtm_type = RTN_UNICAST; - req.rt.rtm_dst_len = target.netmaskBits(); - req.rt.rtm_flags = 0; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -void LinuxNetLink::delRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName) -{ - if (!target) return; - - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - - if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { - fprintf(stderr, "Error binding RTNETLINK (delRoute #1): %s\n", strerror(errno)); - close(fd); - return; - } - -#ifdef ZT_NETLINK_TRACE - char tmp[64]; - char tmp2[64]; - char tmp3[64]; - fprintf(stderr, "Removing Route. target: %s via: %s src: %s iface: %s\n", target.toString(tmp), via.toString(tmp2), src.toString(tmp3), ifaceName); -#endif - - int rtl = sizeof(struct rtmsg); - struct nl_route_req req; - bzero(&req, sizeof(req)); - - struct rtattr *rtap = (struct rtattr *)req.buf; - rtap->rta_type = RTA_DST; - if (target.isV4()) { - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr)); - } else { - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr)); - } - rtl += rtap->rta_len; - - if(via) { - rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len); - rtap->rta_type = RTA_GATEWAY; - if(via.isV4()) { - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr)); - } else { - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr)); - } - rtl += rtap->rta_len; - } else if (src) { - rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len); - rtap->rta_type = RTA_SRC; - if(src.isV4()) { - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&src)->sin_addr, sizeof(struct in_addr)); - - } else { - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&src)->sin6_addr, sizeof(struct in6_addr)); - } - req.rt.rtm_src_len = src.netmaskBits(); - } - - if (ifaceName != NULL) { - int interface_index = _indexForInterface(ifaceName); - if (interface_index != -1) { - rtap = (struct rtattr *) (((char*)rtap) + rtap->rta_len); - rtap->rta_type = RTA_OIF; - rtap->rta_len = RTA_LENGTH(sizeof(int)); - memcpy(RTA_DATA(rtap), &interface_index, sizeof(int)); - rtl += rtap->rta_len; - } - } - - req.nl.nlmsg_len = NLMSG_LENGTH(rtl); - req.nl.nlmsg_flags = NLM_F_REQUEST; - req.nl.nlmsg_type = RTM_DELROUTE; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.rt.rtm_family = target.ss_family; - req.rt.rtm_table = RT_TABLE_MAIN; - req.rt.rtm_protocol = RTPROT_STATIC; - req.rt.rtm_scope = RT_SCOPE_UNIVERSE; - req.rt.rtm_type = RTN_UNICAST; - req.rt.rtm_dst_len = target.netmaskBits(); - req.rt.rtm_flags = 0; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -void LinuxNetLink::addAddress(const InetAddress &addr, const char *iface) -{ - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - memset(&la,0,sizeof(la)); - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - if (addr.isV4()) { - la.nl_groups = RTMGRP_IPV4_IFADDR; - } else { - la.nl_groups = RTMGRP_IPV6_IFADDR; - } - - if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { - fprintf(stderr, "Error binding RTNETLINK (addAddress #1): %s\n", strerror(errno)); - close(fd); - return; - } - -#ifdef ZT_NETLINK_TRACE - char tmp[128]; - fprintf(stderr, "Adding IP address %s to interface %s\n", addr.toString(tmp), iface); -#endif - - int interface_index = _indexForInterface(iface); - for (int reps = 0; interface_index == -1 && reps < 10; ++reps) { - Thread::sleep(100); - interface_index = _indexForInterface(iface); - } - - if (interface_index == -1) { - fprintf(stderr, "Unable to find index for interface %s\n", iface); - close(fd); - return; - } - - int rtl = sizeof(struct ifaddrmsg); - struct nl_adr_req req; - bzero(&req, sizeof(struct nl_adr_req)); - - struct rtattr *rtap = (struct rtattr *)req.buf;; - if(addr.isV4()) { - struct sockaddr_in *addr_v4 = (struct sockaddr_in*)&addr; - rtap->rta_type = IFA_ADDRESS; - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); - rtl += rtap->rta_len; - - rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); - rtap->rta_type = IFA_LOCAL; - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); - rtl += rtap->rta_len; - - InetAddress broadcast = addr.broadcast(); - if(broadcast) { - rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len); - struct sockaddr_in *bcast = (struct sockaddr_in*)&broadcast; - rtap->rta_type = IFA_BROADCAST; - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &bcast->sin_addr, sizeof(struct in_addr)); - rtl += rtap->rta_len; - } - } else { //V6 - rtap->rta_type = IFA_ADDRESS; - struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6*)&addr; - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &addr_v6->sin6_addr, sizeof(struct in6_addr)); - rtl += rtap->rta_len; - } - - if (iface) { - rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len); - rtap->rta_type = IFA_LABEL; - rtap->rta_len = RTA_LENGTH(strlen(iface)); - memcpy(RTA_DATA(rtap), iface, strlen(iface)); - rtl += rtap->rta_len; - } - - req.nl.nlmsg_len = NLMSG_LENGTH(rtl); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - req.nl.nlmsg_type = RTM_NEWADDR; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.ifa.ifa_family = addr.ss_family; - req.ifa.ifa_prefixlen = addr.port(); - req.ifa.ifa_flags = IFA_F_PERMANENT; - req.ifa.ifa_scope = 0; - req.ifa.ifa_index = interface_index; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(sockaddr_nl)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -void LinuxNetLink::removeAddress(const InetAddress &addr, const char *iface) -{ - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd == -1) { - fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); - return; - } - - _setSocketTimeout(fd); - - struct sockaddr_nl la; - la.nl_family = AF_NETLINK; - la.nl_pid = 0; //getpid(); - if (addr.isV4()) { - la.nl_groups = RTMGRP_IPV4_IFADDR; - } else { - la.nl_groups = RTMGRP_IPV6_IFADDR; - } - if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { - fprintf(stderr, "Error binding RTNETLINK (removeAddress #1): %s\n", strerror(errno)); - close(fd); - return; - } - -#ifdef ZT_NETLINK_TRACE - char tmp[128]; - fprintf(stderr, "Removing IP address %s from interface %s\n", addr.toString(tmp), iface); -#endif - - int interface_index = _indexForInterface(iface); - - if (interface_index == -1) { - fprintf(stderr, "Unable to find index for interface %s\n", iface); - close(fd); - return; - } - - int rtl = sizeof(struct ifaddrmsg); - struct nl_adr_req req; - bzero(&req, sizeof(struct nl_adr_req)); - - struct rtattr *rtap = (struct rtattr *)req.buf; - if(addr.isV4()) { - struct sockaddr_in *addr_v4 = (struct sockaddr_in*)&addr; - rtap->rta_type = IFA_ADDRESS; - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); - rtl += rtap->rta_len; - - rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); - rtap->rta_type = IFA_LOCAL; - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); - rtl += rtap->rta_len; - - InetAddress broadcast = addr.broadcast(); - if(broadcast) { - rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len); - struct sockaddr_in *bcast = (struct sockaddr_in*)&broadcast; - rtap->rta_type = IFA_BROADCAST; - rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); - memcpy(RTA_DATA(rtap), &bcast->sin_addr, sizeof(struct in_addr)); - rtl += rtap->rta_len; - } - } else { //V6 - rtap->rta_type = IFA_ADDRESS; - struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6*)&addr; - rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); - memcpy(RTA_DATA(rtap), &addr_v6->sin6_addr, sizeof(struct in6_addr)); - rtl += rtap->rta_len; - } - - if (iface) { - rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len); - rtap->rta_type = IFA_LABEL; - rtap->rta_len = RTA_LENGTH(strlen(iface)); - memcpy(RTA_DATA(rtap), iface, strlen(iface)); - rtl += rtap->rta_len; - } - - req.nl.nlmsg_len = NLMSG_LENGTH(rtl); - req.nl.nlmsg_flags = NLM_F_REQUEST; - req.nl.nlmsg_type = RTM_DELADDR; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.ifa.ifa_family = addr.ss_family; - req.ifa.ifa_prefixlen = addr.port(); - req.ifa.ifa_flags = IFA_F_PERMANENT; - req.ifa.ifa_scope = 0; - req.ifa.ifa_index = interface_index; - - struct sockaddr_nl pa; - bzero(&pa, sizeof(sockaddr_nl)); - pa.nl_family = AF_NETLINK; - - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); - - struct iovec iov; - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - sendmsg(fd, &msg, 0); - - _doRecv(fd); - - close(fd); -} - -bool LinuxNetLink::routeIsSet(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifname) -{ - Mutex::Lock rl(_routes_m); - const std::set &rs = _routes[target]; - for(std::set::const_iterator ri(rs.begin());ri!=rs.end();++ri) { - if ((ri->via == via)&&(ri->src == src)) { - if (ifname) { - Mutex::Lock ifl(_if_m); - const iface_entry *ife = _interfaces.get(ri->ifidx); - if ((ife)&&(!strncmp(ife->ifacename,ifname,IFNAMSIZ))) - return true; - } else { - return true; - } - } - } - return false; -} - -int LinuxNetLink::_indexForInterface(const char *iface) -{ - Mutex::Lock l(_if_m); - int interface_index = -1; - Hashtable::Iterator iter(_interfaces); - int *k = NULL; - iface_entry *v = NULL; - while(iter.next(k,v)) { - if(strcmp(iface, v->ifacename) == 0) { - interface_index = v->index; - break; - } - } - return interface_index; -} - -} // namespace ZeroTier - -#endif diff --git a/osdep/LinuxNetLink.hpp b/osdep/LinuxNetLink.hpp deleted file mode 100644 index 22222e468..000000000 --- a/osdep/LinuxNetLink.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_LINUX_NETLINK_HPP -#define ZT_LINUX_NETLINK_HPP - -#include "../node/Constants.hpp" - -#ifdef __LINUX__ - -#include -#include -#include - -#include -#include -#include -#include -//#include - -#include "../node/InetAddress.hpp" -#include "../node/MAC.hpp" -#include "Thread.hpp" -#include "../node/Hashtable.hpp" -#include "../node/Mutex.hpp" - - -namespace ZeroTier { - -/** - * Interface with Linux's RTNETLINK - */ -class LinuxNetLink -{ -private: - LinuxNetLink(); - ~LinuxNetLink(); - -public: - struct Route { - InetAddress target; - InetAddress via; - InetAddress src; - int ifidx; - - inline bool operator==(const Route &r) const - { return ((target == r.target)&&(via == r.via)&&(src == r.src)&&(ifidx == r.ifidx)); } - inline bool operator!=(const Route &r) const - { return (!(*this == r)); } - inline bool operator<(const Route &r) const - { - if (target < r.target) { - return true; - } else if (target == r.target) { - if (via < r.via) { - return true; - } else if (via == r.via) { - if (src < r.src) { - return true; - } else if (src == r.src) { - return (ifidx < r.ifidx); - } - } - } - return false; - } - inline bool operator>(const Route &r) const - { return (r < *this); } - inline bool operator<=(const Route &r) const - { return !(r < *this); } - inline bool operator>=(const Route &r) const - { return !(*this < r); } - }; - - static LinuxNetLink& getInstance() - { - static LinuxNetLink instance; - return instance; - } - - LinuxNetLink(LinuxNetLink const&) = delete; - void operator=(LinuxNetLink const&) = delete; - - void addRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName); - void delRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName); - - void addAddress(const InetAddress &addr, const char *iface); - void removeAddress(const InetAddress &addr, const char *iface); - - bool routeIsSet(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifname); - - void threadMain() throw(); - -private: - int _doRecv(int fd); - - void _processMessage(struct nlmsghdr *nlp, int nll); - void _routeAdded(struct nlmsghdr *nlp); - void _routeDeleted(struct nlmsghdr *nlp); - void _linkAdded(struct nlmsghdr *nlp); - void _linkDeleted(struct nlmsghdr *nlp); - void _ipAddressAdded(struct nlmsghdr *nlp); - void _ipAddressDeleted(struct nlmsghdr *nlp); - - void _requestInterfaceList(); - void _requestIPv4Routes(); - void _requestIPv6Routes(); - - int _indexForInterface(const char *iface); - - void _setSocketTimeout(int fd, int seconds = 1); - - Thread _t; - bool _running; - - uint32_t _seq; - - std::map< InetAddress,std::set > _routes; - Mutex _routes_m; - - struct iface_entry { - iface_entry() - { memset(this,0,sizeof(iface_entry)); } - int index; - char ifacename[16]; // IFNAMSIZ on Linux == 16 - char mac[18]; - char mac_bin[6]; - unsigned int mtu; - }; - Hashtable _interfaces; - Mutex _if_m; - - // socket communication vars; - int _fd; - struct sockaddr_nl _la; -}; - -} - -#endif - -#endif // ZT_LINUX_NETLINK_HPPS \ No newline at end of file diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp index b8e117c9c..c48340f5f 100644 --- a/osdep/ManagedRoute.cpp +++ b/osdep/ManagedRoute.cpp @@ -49,10 +49,11 @@ #include #include "ManagedRoute.hpp" +#ifdef __LINUX__ +#include "LinuxNetLink.hpp" +#endif #define ZT_BSD_ROUTE_CMD "/sbin/route" -#define ZT_LINUX_IP_COMMAND "/sbin/ip" -#define ZT_LINUX_IP_COMMAND_2 "/usr/sbin/ip" namespace ZeroTier { @@ -269,26 +270,7 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress #ifdef __LINUX__ // ---------------------------------------------------------- #define ZT_ROUTING_SUPPORT_FOUND 1 -static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *localInterface) -{ - long p = (long)fork(); - if (p > 0) { - int exitcode = -1; - ::waitpid(p,&exitcode,0); - } else if (p == 0) { - ::close(STDOUT_FILENO); - ::close(STDERR_FILENO); - char ipbuf[64],ipbuf2[64]; - if (via) { - ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0); - ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0); - } else if ((localInterface)&&(localInterface[0])) { - ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0); - ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0); - } - ::_exit(-1); - } -} +// This has been replaced by LinuxNetLink #endif // __LINUX__ ---------------------------------------------------------- @@ -393,15 +375,19 @@ ManagedRoute::ManagedRoute(const InetAddress &target,const InetAddress &via,cons _target = target; _via = via; _src = src; - if (via.ss_family == AF_INET) + + if (_via.ss_family == AF_INET) { _via.setPort(32); - else if (via.ss_family == AF_INET6) + } else if (_via.ss_family == AF_INET6) { _via.setPort(128); - if (src.ss_family == AF_INET) { + } + + if (_src.ss_family == AF_INET) { _src.setPort(32); - } else if (src.ss_family == AF_INET6) { + } else if (_src.ss_family == AF_INET6) { _src.setPort(128); } + Utils::scopy(_device,sizeof(_device),device); _systemDevice[0] = (char)0; } @@ -508,13 +494,14 @@ bool ManagedRoute::sync() #ifdef __LINUX__ // ---------------------------------------------------------- - if (!_applied.count(leftt)) { + const char *const devptr = (_via) ? (const char *)0 : _device; + if ((leftt)&&(!LinuxNetLink::getInstance().routeIsSet(leftt,_via,_src,devptr))) { _applied[leftt] = false; // boolean unused - _routeCmd("replace",leftt,_via,(_via) ? (const char *)0 : _device); + LinuxNetLink::getInstance().addRoute(leftt, _via, _src, devptr); } - if ((rightt)&&(!_applied.count(rightt))) { + if ((rightt)&&(!LinuxNetLink::getInstance().routeIsSet(rightt,_via,_src,devptr))) { _applied[rightt] = false; // boolean unused - _routeCmd("replace",rightt,_via,(_via) ? (const char *)0 : _device); + LinuxNetLink::getInstance().addRoute(rightt, _via, _src, devptr); } #endif // __LINUX__ ---------------------------------------------------------- @@ -562,7 +549,8 @@ void ManagedRoute::remove() #endif // __BSD__ ------------------------------------------------------------ #ifdef __LINUX__ // ---------------------------------------------------------- - _routeCmd("del",r->first,_via,(_via) ? (const char *)0 : _device); + //_routeCmd("del",r->first,_via,(_via) ? (const char *)0 : _device); + LinuxNetLink::getInstance().delRoute(r->first,_via,_src,(_via) ? (const char *)0 : _device); #endif // __LINUX__ ---------------------------------------------------------- #ifdef __WINDOWS__ // -------------------------------------------------------- diff --git a/osdep/NetBSDEthernetTap.cpp b/osdep/NetBSDEthernetTap.cpp deleted file mode 100644 index 1d4a8ab8f..000000000 --- a/osdep/NetBSDEthernetTap.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "freebsd_getifmaddrs.h" - -#include -#include -#include -#include -#include - -#include "../core/Constants.hpp" -#include "../core/Utils.hpp" -#include "../core/Mutex.hpp" -#include "OSUtils.hpp" -#include "NetBSDEthernetTap.hpp" - -#include -using namespace std; -#define ZT_BASE32_CHARS "0123456789abcdefghijklmnopqrstuv" - -// ff:ff:ff:ff:ff:ff with no ADI -static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); - -namespace ZeroTier { - -NetBSDEthernetTap::NetBSDEthernetTap( - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg) : - _handler(handler), - _arg(arg), - _nwid(nwid), - _mtu(mtu), - _metric(metric), - _fd(0), - _enabled(true) -{ - static Mutex globalTapCreateLock; - char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32]; - struct stat stattmp; - - Mutex::Lock _gl(globalTapCreateLock); - - if (mtu > 2800) - throw std::runtime_error("max tap MTU is 2800"); - - // we can create /dev/tap* - std::vector devFiles(OSUtils::listDirectory("/dev")); - for(int i=9993;i<(9993+128);++i) { - Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); - Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); - if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) { - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"create",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } else throw std::runtime_error("fork() failed"); - - cpid = (long)vfork(); - if (cpid == 0) { - string tmp; - sprintf((char*)tmp.c_str(), "%d", i); - string minor = tmp.c_str(); - ::execl("/sbin/mknod","/sbin/mknod",devpath,"c","169",minor.c_str(),(const char *)0); - // http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/conf/majors - // major 169 => tap - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } else throw std::runtime_error("fork() failed"); - - cerr<<"created device "< 0) - break; - else - throw std::runtime_error("unable to open created tap device "); - } else { - throw std::runtime_error("cannot find /dev node for newly created tap device"); - } - } - } - - if (_fd <= 0) - throw std::runtime_error("unable to open TAP device or no more devices available"); - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - // Configure MAC address and MTU, bring interface up - Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); - Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu); - Utils::snprintf(metstr,sizeof(metstr),"%u",_metric); - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"link",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) { - ::close(_fd); - throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); - } - } - - // ifconfig link seems to be different from address - // https://wiki.netbsd.org/tutorials/faking_a_mac_address/ - cpid = (long)vfork(); - if (cpid == 0) { - string cmdline = "net.link.tap."+string(_dev); - cmdline += "="+string(ethaddr); - ::execl("/sbin/sysctl","/sbin/sysctl","-w",cmdline.c_str(),(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) { - ::close(_fd); - throw std::runtime_error("sysctl failure setting link-layer address and activating tap interface"); - } - } - - // Set close-on-exec so that devices cannot persist if we fork/exec for update - fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); - - ::pipe(_shutdownSignalPipe); - - _thread = Thread::start(this); - -} - -NetBSDEthernetTap::~NetBSDEthernetTap() -{ - ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - Thread::join(_thread); - ::close(_fd); - ::close(_shutdownSignalPipe[0]); - ::close(_shutdownSignalPipe[1]); - - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } - - cpid = (long)vfork(); - if (cpid == 0) { - string tmp="/dev/"; - tmp+=_dev.c_str(); - ::execl("/bin/rm","/bin/rm",tmp.c_str(),(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } else throw std::runtime_error("fork() failed"); -} - -void NetBSDEthernetTap::setEnabled(bool en) -{ - _enabled = en; -} - -bool NetBSDEthernetTap::enabled() const -{ - return _enabled; -} - -static bool ___removeIp(const std::string &_dev,const InetAddress &ip) -{ - long cpid = (long)vfork(); - if (cpid == 0) { - execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0); - _exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; // never reached, make compiler shut up about return value -} - -bool NetBSDEthernetTap::addIp(const InetAddress &ip) -{ - if (!ip) - return false; - - std::vector allIps(ips()); - if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) - return true; // IP/netmask already assigned - - // Remove and reconfigure if address is the same but netmask is different - for(std::vector::iterator i(allIps.begin());i!=allIps.end();++i) { - if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { - if (___removeIp(_dev,*i)) - break; - } - } - - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; -} - -bool NetBSDEthernetTap::removeIp(const InetAddress &ip) -{ - if (!ip) - return false; - std::vector allIps(ips()); - if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) { - if (___removeIp(_dev,ip)) - return true; - } - return false; -} - -std::vector NetBSDEthernetTap::ips() const -{ - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (getifaddrs(&ifa)) - return std::vector(); - - std::vector r; - - struct ifaddrs *p = ifa; - while (p) { - if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { - switch(p->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; - struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; - r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); - } break; - case AF_INET6: { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; - struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; - uint32_t b[4]; - memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); - r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); - } break; - } - } - p = p->ifa_next; - } - - if (ifa) - freeifaddrs(ifa); - - std::sort(r.begin(),r.end()); - std::unique(r.begin(),r.end()); - - return r; -} - -void NetBSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - char putBuf[4096]; - if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { - to.copyTo(putBuf,6); - from.copyTo(putBuf + 6,6); - *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); - memcpy(putBuf + 14,data,len); - len += 14; - ::write(_fd,putBuf,len); - } -} - -std::string NetBSDEthernetTap::deviceName() const -{ - return _dev; -} - -void NetBSDEthernetTap::setFriendlyName(const char *friendlyName) -{ -} - -void NetBSDEthernetTap::scanMulticastGroups(std::vector &added,std::vector &removed) -{ - std::vector newGroups; - - struct ifmaddrs *ifmap = (struct ifmaddrs *)0; - if (!getifmaddrs(&ifmap)) { - struct ifmaddrs *p = ifmap; - while (p) { - if (p->ifma_addr->sa_family == AF_LINK) { - struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name; - struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; - if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen))) - newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0)); - } - p = p->ifma_next; - } - freeifmaddrs(ifmap); - } - - std::vector allIps(ips()); - for(std::vector::iterator ip(allIps.begin());ip!=allIps.end();++ip) - newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); - - std::sort(newGroups.begin(),newGroups.end()); - std::unique(newGroups.begin(),newGroups.end()); - - for(std::vector::iterator m(newGroups.begin());m!=newGroups.end();++m) { - if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) - added.push_back(*m); - } - for(std::vector::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { - if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) - removed.push_back(*m); - } - - _multicastGroups.swap(newGroups); -} - -/* -bool NetBSDEthernetTap::updateMulticastGroups(std::set &groups) -{ - std::set newGroups; - struct ifmaddrs *ifmap = (struct ifmaddrs *)0; - if (!getifmaddrs(&ifmap)) { - struct ifmaddrs *p = ifmap; - while (p) { - if (p->ifma_addr->sa_family == AF_LINK) { - struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name; - struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; - if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen))) - newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0)); - } - p = p->ifma_next; - } - freeifmaddrs(ifmap); - } - - { - std::set allIps(ips()); - for(std::set::const_iterator i(allIps.begin());i!=allIps.end();++i) - newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i)); - } - - bool changed = false; - - for(std::set::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) { - if (!groups.count(*mg)) { - groups.insert(*mg); - changed = true; - } - } - for(std::set::iterator mg(groups.begin());mg!=groups.end();) { - if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) { - groups.erase(mg++); - changed = true; - } else ++mg; - } - - return changed; -} -*/ - -void NetBSDEthernetTap::threadMain() - throw() -{ - fd_set readfds,nullfds; - MAC to,from; - int n,nfds,r; - char getBuf[8194]; - - // Wait for a moment after startup -- wait for Network to finish - // constructing itself. - Thread::sleep(500); - - FD_ZERO(&readfds); - FD_ZERO(&nullfds); - nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - - r = 0; - for(;;) { - FD_SET(_shutdownSignalPipe[0],&readfds); - FD_SET(_fd,&readfds); - select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - - if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread - break; - - if (FD_ISSET(_fd,&readfds)) { - n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); - if (n < 0) { - if ((errno != EINTR)&&(errno != ETIMEDOUT)) - break; - } else { - // Some tap drivers like to send the ethernet frame and the - // payload in two chunks, so handle that by accumulating - // data until we have at least a frame. - r += n; - if (r > 14) { - if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms - r = _mtu + 14; - - if (_enabled) { - to.setTo(getBuf,6); - from.setTo(getBuf + 6,6); - unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); - // TODO: VLAN support - _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14); - } - - r = 0; - } - } - } - } -} - -} // namespace ZeroTier diff --git a/osdep/NetBSDEthernetTap.hpp b/osdep/NetBSDEthernetTap.hpp deleted file mode 100644 index 8b3c4528f..000000000 --- a/osdep/NetBSDEthernetTap.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_NetBSDEthernetTap_HPP -#define ZT_NetBSDEthernetTap_HPP - -#include -#include - -#include -#include -#include - -#include "../core/Constants.hpp" -#include "../core/MulticastGroup.hpp" -#include "../core/MAC.hpp" -#include "Thread.hpp" -#include "EthernetTap.hpp" - -namespace ZeroTier { - -class NetBSDEthernetTap : public EthernetTap -{ -public: - NetBSDEthernetTap( - const char *homePath, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg); - - virtual ~NetBSDEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIp(const InetAddress &ip); - virtual bool removeIp(const InetAddress &ip); - virtual std::vector ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual void scanMulticastGroups(std::vector &added,std::vector &removed); - - void threadMain() - throw(); - -private: - void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); - void *_arg; - uint64_t _nwid; - Thread _thread; - std::string _dev; - std::vector _multicastGroups; - unsigned int _mtu; - unsigned int _metric; - int _fd; - int _shutdownSignalPipe[2]; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp deleted file mode 100644 index 956d3a5d4..000000000 --- a/osdep/WindowsEthernetTap.cpp +++ /dev/null @@ -1,1304 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#define _WIN32_WINNT 0x06010000 - -#include "../core/Constants.hpp" - -#ifdef __WINDOWS__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../core/Utils.hpp" -#include "../core/Mutex.hpp" - -#include "WindowsEthernetTap.hpp" -#include "OSUtils.hpp" - -#include "../installsupport/windows/tap-windows-ndis6/src/tap-windows-ndis6/tap-windows.h" - -#include - -// Create a fake unused default route to force detection of network type on networks without gateways -#define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE - -// Function signatures of dynamically loaded functions, from newdev.h, setupapi.h, and cfgmgr32.h -typedef BOOL (WINAPI *UpdateDriverForPlugAndPlayDevicesA_t)(_In_opt_ HWND hwndParent,_In_ LPCSTR HardwareId,_In_ LPCSTR FullInfPath,_In_ DWORD InstallFlags,_Out_opt_ PBOOL bRebootRequired); -typedef BOOL (WINAPI *SetupDiGetINFClassA_t)(_In_ PCSTR InfName,_Out_ LPGUID ClassGuid,_Out_writes_(ClassNameSize) PSTR ClassName,_In_ DWORD ClassNameSize,_Out_opt_ PDWORD RequiredSize); -typedef HDEVINFO (WINAPI *SetupDiCreateDeviceInfoList_t)(_In_opt_ CONST GUID *ClassGuid,_In_opt_ HWND hwndParent); -typedef BOOL (WINAPI *SetupDiCreateDeviceInfoA_t)(_In_ HDEVINFO DeviceInfoSet,_In_ PCSTR DeviceName,_In_ CONST GUID *ClassGuid,_In_opt_ PCSTR DeviceDescription,_In_opt_ HWND hwndParent,_In_ DWORD CreationFlags,_Out_opt_ PSP_DEVINFO_DATA DeviceInfoData); -typedef BOOL (WINAPI *SetupDiSetDeviceRegistryPropertyA_t)(_In_ HDEVINFO DeviceInfoSet,_Inout_ PSP_DEVINFO_DATA DeviceInfoData,_In_ DWORD Property,_In_reads_bytes_opt_(PropertyBufferSize) CONST BYTE *PropertyBuffer,_In_ DWORD PropertyBufferSize); -typedef BOOL (WINAPI *SetupDiCallClassInstaller_t)(_In_ DI_FUNCTION InstallFunction,_In_ HDEVINFO DeviceInfoSet,_In_opt_ PSP_DEVINFO_DATA DeviceInfoData); -typedef BOOL (WINAPI *SetupDiDestroyDeviceInfoList_t)(_In_ HDEVINFO DeviceInfoSet); -typedef HDEVINFO (WINAPI *SetupDiGetClassDevsExA_t)(_In_opt_ CONST GUID *ClassGuid,_In_opt_ PCSTR Enumerator,_In_opt_ HWND hwndParent,_In_ DWORD Flags,_In_opt_ HDEVINFO DeviceInfoSet,_In_opt_ PCSTR MachineName,_Reserved_ PVOID Reserved); -typedef BOOL (WINAPI *SetupDiOpenDeviceInfoA_t)(_In_ HDEVINFO DeviceInfoSet,_In_ PCSTR DeviceInstanceId,_In_opt_ HWND hwndParent,_In_ DWORD OpenFlags,_Out_opt_ PSP_DEVINFO_DATA DeviceInfoData); -typedef BOOL (WINAPI *SetupDiEnumDeviceInfo_t)(_In_ HDEVINFO DeviceInfoSet,_In_ DWORD MemberIndex,_Out_ PSP_DEVINFO_DATA DeviceInfoData); -typedef BOOL (WINAPI *SetupDiSetClassInstallParamsA_t)(_In_ HDEVINFO DeviceInfoSet,_In_opt_ PSP_DEVINFO_DATA DeviceInfoData,_In_reads_bytes_opt_(ClassInstallParamsSize) PSP_CLASSINSTALL_HEADER ClassInstallParams,_In_ DWORD ClassInstallParamsSize); -typedef CONFIGRET (WINAPI *CM_Get_Device_ID_ExA_t)(_In_ DEVINST dnDevInst,_Out_writes_(BufferLen) PSTR Buffer,_In_ ULONG BufferLen,_In_ ULONG ulFlags,_In_opt_ HMACHINE hMachine); -typedef BOOL (WINAPI *SetupDiGetDeviceInstanceIdA_t)(_In_ HDEVINFO DeviceInfoSet,_In_ PSP_DEVINFO_DATA DeviceInfoData,_Out_writes_opt_(DeviceInstanceIdSize) PSTR DeviceInstanceId,_In_ DWORD DeviceInstanceIdSize,_Out_opt_ PDWORD RequiredSize); - -namespace ZeroTier { - -namespace { - -// Static/singleton class that when initialized loads a bunch of environment information and a few dynamically loaded DLLs -class WindowsEthernetTapEnv -{ -public: - WindowsEthernetTapEnv() - { -#ifdef _WIN64 - is64Bit = TRUE; - tapDriverPath = "\\tap-windows\\x64\\zttap300.inf"; -#else - is64Bit = FALSE; - IsWow64Process(GetCurrentProcess(),&is64Bit); - if (is64Bit) { - fprintf(stderr,"FATAL: you must use the 64-bit ZeroTier One service on 64-bit Windows systems\r\n"); - _exit(1); - } - tapDriverPath = "\\tap-windows\\x86\\zttap300.inf"; -#endif - tapDriverName = "zttap300"; - - setupApiMod = LoadLibraryA("setupapi.dll"); - if (!setupApiMod) { - fprintf(stderr,"FATAL: unable to dynamically load setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiGetINFClassA = (SetupDiGetINFClassA_t)GetProcAddress(setupApiMod,"SetupDiGetINFClassA"))) { - fprintf(stderr,"FATAL: SetupDiGetINFClassA not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiCreateDeviceInfoList = (SetupDiCreateDeviceInfoList_t)GetProcAddress(setupApiMod,"SetupDiCreateDeviceInfoList"))) { - fprintf(stderr,"FATAL: SetupDiCreateDeviceInfoList not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiCreateDeviceInfoA = (SetupDiCreateDeviceInfoA_t)GetProcAddress(setupApiMod,"SetupDiCreateDeviceInfoA"))) { - fprintf(stderr,"FATAL: SetupDiCreateDeviceInfoA not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiSetDeviceRegistryPropertyA = (SetupDiSetDeviceRegistryPropertyA_t)GetProcAddress(setupApiMod,"SetupDiSetDeviceRegistryPropertyA"))) { - fprintf(stderr,"FATAL: SetupDiSetDeviceRegistryPropertyA not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiCallClassInstaller = (SetupDiCallClassInstaller_t)GetProcAddress(setupApiMod,"SetupDiCallClassInstaller"))) { - fprintf(stderr,"FATAL: SetupDiCallClassInstaller not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiDestroyDeviceInfoList = (SetupDiDestroyDeviceInfoList_t)GetProcAddress(setupApiMod,"SetupDiDestroyDeviceInfoList"))) { - fprintf(stderr,"FATAL: SetupDiDestroyDeviceInfoList not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiGetClassDevsExA = (SetupDiGetClassDevsExA_t)GetProcAddress(setupApiMod,"SetupDiGetClassDevsExA"))) { - fprintf(stderr,"FATAL: SetupDiGetClassDevsExA not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiOpenDeviceInfoA = (SetupDiOpenDeviceInfoA_t)GetProcAddress(setupApiMod,"SetupDiOpenDeviceInfoA"))) { - fprintf(stderr,"FATAL: SetupDiOpenDeviceInfoA not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiEnumDeviceInfo = (SetupDiEnumDeviceInfo_t)GetProcAddress(setupApiMod,"SetupDiEnumDeviceInfo"))) { - fprintf(stderr,"FATAL: SetupDiEnumDeviceInfo not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiSetClassInstallParamsA = (SetupDiSetClassInstallParamsA_t)GetProcAddress(setupApiMod,"SetupDiSetClassInstallParamsA"))) { - fprintf(stderr,"FATAL: SetupDiSetClassInstallParamsA not found in setupapi.dll\r\n"); - _exit(1); - } - if (!(this->SetupDiGetDeviceInstanceIdA = (SetupDiGetDeviceInstanceIdA_t)GetProcAddress(setupApiMod,"SetupDiGetDeviceInstanceIdA"))) { - fprintf(stderr,"FATAL: SetupDiGetDeviceInstanceIdA not found in setupapi.dll\r\n"); - _exit(1); - } - - newDevMod = LoadLibraryA("newdev.dll"); - if (!newDevMod) { - fprintf(stderr,"FATAL: unable to dynamically load newdev.dll\r\n"); - _exit(1); - } - if (!(this->UpdateDriverForPlugAndPlayDevicesA = (UpdateDriverForPlugAndPlayDevicesA_t)GetProcAddress(newDevMod,"UpdateDriverForPlugAndPlayDevicesA"))) { - fprintf(stderr,"FATAL: UpdateDriverForPlugAndPlayDevicesA not found in newdev.dll\r\n"); - _exit(1); - } - - cfgMgrMod = LoadLibraryA("cfgmgr32.dll"); - if (!cfgMgrMod) { - fprintf(stderr,"FATAL: unable to dynamically load cfgmgr32.dll\r\n"); - _exit(1); - } - if (!(this->CM_Get_Device_ID_ExA = (CM_Get_Device_ID_ExA_t)GetProcAddress(cfgMgrMod,"CM_Get_Device_ID_ExA"))) { - fprintf(stderr,"FATAL: CM_Get_Device_ID_ExA not found in cfgmgr32.dll\r\n"); - _exit(1); - } - } - - BOOL is64Bit; // is the system 64-bit, regardless of whether this binary is or not - std::string tapDriverPath; - std::string tapDriverName; - - UpdateDriverForPlugAndPlayDevicesA_t UpdateDriverForPlugAndPlayDevicesA; - - SetupDiGetINFClassA_t SetupDiGetINFClassA; - SetupDiCreateDeviceInfoList_t SetupDiCreateDeviceInfoList; - SetupDiCreateDeviceInfoA_t SetupDiCreateDeviceInfoA; - SetupDiSetDeviceRegistryPropertyA_t SetupDiSetDeviceRegistryPropertyA; - SetupDiCallClassInstaller_t SetupDiCallClassInstaller; - SetupDiDestroyDeviceInfoList_t SetupDiDestroyDeviceInfoList; - SetupDiGetClassDevsExA_t SetupDiGetClassDevsExA; - SetupDiOpenDeviceInfoA_t SetupDiOpenDeviceInfoA; - SetupDiEnumDeviceInfo_t SetupDiEnumDeviceInfo; - SetupDiSetClassInstallParamsA_t SetupDiSetClassInstallParamsA; - SetupDiGetDeviceInstanceIdA_t SetupDiGetDeviceInstanceIdA; - - CM_Get_Device_ID_ExA_t CM_Get_Device_ID_ExA; - -private: - HMODULE setupApiMod; - HMODULE newDevMod; - HMODULE cfgMgrMod; -}; -static const WindowsEthernetTapEnv WINENV; - -// Only create or delete devices one at a time -static Mutex _systemTapInitLock; - -// Only perform installation or uninstallation options one at a time -static Mutex _systemDeviceManagementLock; - -} // anonymous namespace - -std::string WindowsEthernetTap::addNewPersistentTapDevice(const char *pathToInf,std::string &deviceInstanceId) -{ - Mutex::Lock _l(_systemDeviceManagementLock); - - GUID classGuid; - char className[1024]; - if (!WINENV.SetupDiGetINFClassA(pathToInf,&classGuid,className,sizeof(className),(PDWORD)0)) { - return std::string("SetupDiGetINFClassA() failed -- unable to read zttap driver INF file"); - } - - HDEVINFO deviceInfoSet = WINENV.SetupDiCreateDeviceInfoList(&classGuid,(HWND)0); - if (deviceInfoSet == INVALID_HANDLE_VALUE) { - return std::string("SetupDiCreateDeviceInfoList() failed"); - } - - SP_DEVINFO_DATA deviceInfoData; - memset(&deviceInfoData,0,sizeof(deviceInfoData)); - deviceInfoData.cbSize = sizeof(deviceInfoData); - if (!WINENV.SetupDiCreateDeviceInfoA(deviceInfoSet,className,&classGuid,(PCSTR)0,(HWND)0,DICD_GENERATE_ID,&deviceInfoData)) { - WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet); - return std::string("SetupDiCreateDeviceInfoA() failed"); - } - - if (!WINENV.SetupDiSetDeviceRegistryPropertyA(deviceInfoSet,&deviceInfoData,SPDRP_HARDWAREID,(const BYTE *)WINENV.tapDriverName.c_str(),(DWORD)(WINENV.tapDriverName.length() + 1))) { - WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet); - return std::string("SetupDiSetDeviceRegistryPropertyA() failed"); - } - - if (!WINENV.SetupDiCallClassInstaller(DIF_REGISTERDEVICE,deviceInfoSet,&deviceInfoData)) { - WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet); - return std::string("SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed"); - } - - // HACK: During upgrades, this can fail while the installer is still running. So make 60 attempts - // with a 1s delay between each attempt. - bool driverInstalled = false; - for(int retryCounter=0;retryCounter<60;++retryCounter) { - BOOL rebootRequired = FALSE; - if (WINENV.UpdateDriverForPlugAndPlayDevicesA((HWND)0,WINENV.tapDriverName.c_str(),pathToInf,INSTALLFLAG_FORCE|INSTALLFLAG_NONINTERACTIVE,&rebootRequired)) { - driverInstalled = true; - break; - } else Sleep(1000); - } - if (!driverInstalled) { - WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet); - return std::string("UpdateDriverForPlugAndPlayDevices() failed (made 60 attempts)"); - } - - char iidbuf[1024]; - DWORD iidReqSize = sizeof(iidbuf); - if (WINENV.SetupDiGetDeviceInstanceIdA(deviceInfoSet,&deviceInfoData,iidbuf,sizeof(iidbuf),&iidReqSize)) { - deviceInstanceId = iidbuf; - } // failure here is not fatal since we only need this on Vista and 2008 -- other versions fill it into the registry automatically - - WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet); - - return std::string(); -} - -std::string WindowsEthernetTap::destroyAllLegacyPersistentTapDevices() -{ - char subkeyName[1024]; - char subkeyClass[1024]; - char data[1024]; - - std::set 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 std::string("Could not open registry key"); - - 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))&&(WINENV.tapDriverName != data)) { - 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); - } - - std::string errlist; - for(std::set::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) { - std::string err = deletePersistentTapDevice(iidp->c_str()); - if (err.length() > 0) { - if (errlist.length() > 0) - errlist.push_back(','); - errlist.append(err); - } - } - return errlist; -} - -std::string WindowsEthernetTap::destroyAllPersistentTapDevices() -{ - char subkeyName[1024]; - char subkeyClass[1024]; - char data[1024]; - - std::set 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 std::string("Could not open registry key"); - - 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); - } - - std::string errlist; - for(std::set::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) { - std::string err = deletePersistentTapDevice(iidp->c_str()); - if (err.length() > 0) { - if (errlist.length() > 0) - errlist.push_back(','); - errlist.append(err); - } - } - return errlist; -} - -std::string WindowsEthernetTap::deletePersistentTapDevice(const char *instanceId) -{ - char iid[256]; - SP_REMOVEDEVICE_PARAMS rmdParams; - - memset(&rmdParams,0,sizeof(rmdParams)); - rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); - rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE; - rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL; - rmdParams.HwProfile = 0; - - Mutex::Lock _l(_systemDeviceManagementLock); - - HDEVINFO devInfo = WINENV.SetupDiGetClassDevsExA((const GUID *)0,(PCSTR)0,(HWND)0,DIGCF_ALLCLASSES,(HDEVINFO)0,(PCSTR)0,(PVOID)0); - if (devInfo == INVALID_HANDLE_VALUE) - return std::string("SetupDiGetClassDevsExA() failed"); - WINENV.SetupDiOpenDeviceInfoA(devInfo,instanceId,(HWND)0,0,(PSP_DEVINFO_DATA)0); - - SP_DEVINFO_DATA devInfoData; - memset(&devInfoData,0,sizeof(devInfoData)); - devInfoData.cbSize = sizeof(devInfoData); - for(DWORD devIndex=0;WINENV.SetupDiEnumDeviceInfo(devInfo,devIndex,&devInfoData);devIndex++) { - if ((WINENV.CM_Get_Device_ID_ExA(devInfoData.DevInst,iid,sizeof(iid),0,(HMACHINE)0) == CR_SUCCESS)&&(!strcmp(iid,instanceId))) { - if (!WINENV.SetupDiSetClassInstallParamsA(devInfo,&devInfoData,&rmdParams.ClassInstallHeader,sizeof(rmdParams))) { - WINENV.SetupDiDestroyDeviceInfoList(devInfo); - return std::string("SetupDiSetClassInstallParams() failed"); - } - - if (!WINENV.SetupDiCallClassInstaller(DIF_REMOVE,devInfo,&devInfoData)) { - WINENV.SetupDiDestroyDeviceInfoList(devInfo); - return std::string("SetupDiCallClassInstaller(DIF_REMOVE) failed"); - } - - WINENV.SetupDiDestroyDeviceInfoList(devInfo); - return std::string(); - } - } - - WINENV.SetupDiDestroyDeviceInfoList(devInfo); - return std::string("instance ID not found"); -} - -bool WindowsEthernetTap::setPersistentTapDeviceState(const char *instanceId,bool enabled) -{ - char iid[256]; - SP_PROPCHANGE_PARAMS params; - - Mutex::Lock _l(_systemDeviceManagementLock); - - HDEVINFO devInfo = WINENV.SetupDiGetClassDevsExA((const GUID *)0,(PCSTR)0,(HWND)0,DIGCF_ALLCLASSES,(HDEVINFO)0,(PCSTR)0,(PVOID)0); - if (devInfo == INVALID_HANDLE_VALUE) - return false; - WINENV.SetupDiOpenDeviceInfoA(devInfo,instanceId,(HWND)0,0,(PSP_DEVINFO_DATA)0); - - SP_DEVINFO_DATA devInfoData; - memset(&devInfoData,0,sizeof(devInfoData)); - devInfoData.cbSize = sizeof(devInfoData); - for(DWORD devIndex=0;WINENV.SetupDiEnumDeviceInfo(devInfo,devIndex,&devInfoData);devIndex++) { - if ((WINENV.CM_Get_Device_ID_ExA(devInfoData.DevInst,iid,sizeof(iid),0,(HMACHINE)0) == CR_SUCCESS)&&(!strcmp(iid,instanceId))) { - memset(¶ms,0,sizeof(params)); - params.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); - params.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; - params.StateChange = enabled ? DICS_ENABLE : DICS_DISABLE; - params.Scope = DICS_FLAG_GLOBAL; - params.HwProfile = 0; - - WINENV.SetupDiSetClassInstallParamsA(devInfo,&devInfoData,¶ms.ClassInstallHeader,sizeof(params)); - WINENV.SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,devInfo,&devInfoData); - - memset(¶ms,0,sizeof(params)); - params.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); - params.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; - params.StateChange = enabled ? DICS_ENABLE : DICS_DISABLE; - params.Scope = DICS_FLAG_CONFIGSPECIFIC; - params.HwProfile = 0; - - WINENV.SetupDiSetClassInstallParamsA(devInfo,&devInfoData,¶ms.ClassInstallHeader,sizeof(params)); - WINENV.SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,devInfo,&devInfoData); - - WINENV.SetupDiDestroyDeviceInfoList(devInfo); - return true; - } - } - - WINENV.SetupDiDestroyDeviceInfoList(devInfo); - return false; -} - -WindowsEthernetTap::WindowsEthernetTap( - const char *hp, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg) : - _handler(handler), - _arg(arg), - _mac(mac), - _nwid(nwid), - _mtu(mtu), - _tap(INVALID_HANDLE_VALUE), - _friendlyName(friendlyName), - _injectSemaphore(INVALID_HANDLE_VALUE), - _pathToHelpers(hp), - _run(true), - _initialized(false), - _enabled(true) -{ - char subkeyName[1024]; - char subkeyClass[1024]; - char data[1024]; - char tag[24]; - - // We "tag" registry entries with the network ID to identify persistent devices - OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid); - - Mutex::Lock _l(_systemTapInitLock); - - HKEY nwAdapters; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS) - throw std::runtime_error("unable to open registry key for network adapter enumeration"); - - // Look for the tap instance that corresponds with this network - 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] = (char)0; - - if (WINENV.tapDriverName == data) { - 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); - - 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 ((_netCfgInstanceId.length() == 0)&&(instanceId.length() != 0)&&(instanceIdPath.length() != 0)) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - data[dataLen] = '\0'; - if (!strcmp(data,tag)) { - _netCfgInstanceId = instanceId; - _deviceInstanceId = instanceIdPath; - - _mySubkeyName = subkeyName; - break; // found it! - } - } - } - } - } - } else break; // no more subkeys or error occurred enumerating them - } - - // If there is no device, try to create one - bool creatingNewDevice = (_netCfgInstanceId.length() == 0); - std::string newDeviceInstanceId; - if (creatingNewDevice) { - for(int getNewAttemptCounter=0;getNewAttemptCounter<2;++getNewAttemptCounter) { - 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 (WINENV.tapDriverName == data) { - type = 0; - dataLen = sizeof(data); - if ((RegGetValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",RRF_RT_ANY,&type,(PVOID)data,&dataLen) != ERROR_SUCCESS)||(dataLen <= 0)) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - RegSetKeyValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",REG_SZ,tag,(DWORD)(strlen(tag)+1)); - - _netCfgInstanceId.assign(data,dataLen); - - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) - _deviceInstanceId.assign(data,dataLen); - - _mySubkeyName = subkeyName; - - // Disable DHCP by default on new 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; - RegSetKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable)); - RegCloseKey(tcpIpInterfaces); - } - - break; // found an unused zttap device - } - } - } - } - } else break; // no more keys or error occurred - } - - if (_netCfgInstanceId.length() > 0) { - break; // found an unused zttap device - } else { - // no unused zttap devices, so create one - std::string errm = addNewPersistentTapDevice((std::string(_pathToHelpers) + WINENV.tapDriverPath).c_str(),newDeviceInstanceId); - if (errm.length() > 0) - throw std::runtime_error(std::string("unable to create new device instance: ")+errm); - } - } - } - - if (_netCfgInstanceId.length() > 0) { - char tmps[64]; - unsigned int tmpsl = OSUtils::ztsnprintf(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; - RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl); - RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl); - tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu); - RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl); - - DWORD tmp = 0; - 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) { - // Vista/2008 does not set this - if (newDeviceInstanceId.length() > 0) - RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length()); - - // Set EnableDHCP to 0 by default on new devices - tmp = 0; - RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); - } - RegCloseKey(nwAdapters); - } else { - RegCloseKey(nwAdapters); - throw std::runtime_error("unable to find or create tap adapter"); - } - - { - char nobraces[128]; // strip braces from GUID before converting it, because Windows - const char *nbtmp1 = _netCfgInstanceId.c_str(); - 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?)"); - } - - // Get the LUID, which is one of like 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"); - - //_initialized = true; - - if (friendlyName) - setFriendlyName(friendlyName); - - _injectSemaphore = CreateSemaphore(NULL,0,1,NULL); - _thread = Thread::start(this); -} - -WindowsEthernetTap::~WindowsEthernetTap() -{ - _run = false; - ReleaseSemaphore(_injectSemaphore,1,NULL); - Thread::join(_thread); - CloseHandle(_injectSemaphore); - setPersistentTapDeviceState(_deviceInstanceId.c_str(),false); -} - -void WindowsEthernetTap::setEnabled(bool en) -{ - _enabled = en; -} - -bool WindowsEthernetTap::enabled() const -{ - return _enabled; -} - -bool WindowsEthernetTap::addIp(const InetAddress &ip) -{ - if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT? - return false; - - Mutex::Lock _l(_assignedIps_m); - if (std::find(_assignedIps.begin(),_assignedIps.end(),ip) != _assignedIps.end()) - return true; - _assignedIps.push_back(ip); - _syncIps(); - return true; -} - -bool WindowsEthernetTap::removeIp(const InetAddress &ip) -{ - if (ip.isV6()) - return true; - - { - Mutex::Lock _l(_assignedIps_m); - std::vector::iterator aip(std::find(_assignedIps.begin(),_assignedIps.end(),ip)); - if (aip != _assignedIps.end()) - _assignedIps.erase(aip); - } - - if (!_initialized) - return false; - - try { - MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; - if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { - if ((ipt)&&(ipt->NumEntries > 0)) { - for(DWORD i=0;i<(DWORD)ipt->NumEntries;++i) { - if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - 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); - if (addr.ipScope() == InetAddress::IP_SCOPE_LINK_LOCAL) - continue; // can't remove link-local IPv6 addresses - break; - } - if (addr == ip) { - DeleteUnicastIpAddressEntry(&(ipt->Table[i])); - FreeMibTable(ipt); - - if (ip.isV4()) { - std::vector regIps(_getRegistryIPv4Value("IPAddress")); - std::vector regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); - char ipbuf[64]; - std::string ipstr(ip.toIpString(ipbuf)); - 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; - } - } - } - } - FreeMibTable((PVOID)ipt); - } - } catch ( ... ) {} - return false; -} - -std::vector WindowsEthernetTap::ips() const -{ - static const InetAddress linkLocalLoopback("fe80::1/64"); // what is this and why does Windows assign it? - std::vector addrs; - - if (!_initialized) - return addrs; - - try { - MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; - if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { - if ((ipt)&&(ipt->NumEntries > 0)) { - for(DWORD i=0;i<(DWORD)ipt->NumEntries;++i) { - if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - switch(ipt->Table[i].Address.si_family) { - 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.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.push_back(ip); - } break; - } - } - } - } - FreeMibTable(ipt); - } - } catch ( ... ) {} // sanity check, shouldn't happen unless out of memory - - std::sort(addrs.begin(),addrs.end()); - addrs.erase(std::unique(addrs.begin(),addrs.end()),addrs.end()); - - return addrs; -} - -void WindowsEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > _mtu)) - return; - - Mutex::Lock _l(_injectPending_m); - _injectPending.emplace(); - _injectPending.back().len = len + 14; - char *const d = _injectPending.back().data; - to.copyTo((uint8_t *)d); - from.copyTo((uint8_t *)(d + 6)); - d[12] = (char)((etherType >> 8U) & 0xffU); - d[13] = (char)(etherType & 0xffU); - memcpy(d + 14,data,len); - - ReleaseSemaphore(_injectSemaphore,1,NULL); -} - -std::string WindowsEthernetTap::deviceName() const -{ - char tmp[1024]; - if (ConvertInterfaceLuidToNameA(&_deviceLuid,tmp,sizeof(tmp)) != NO_ERROR) - return std::string("[ConvertInterfaceLuidToName() failed]"); - return std::string(tmp); -} - -void WindowsEthernetTap::setFriendlyName(const char *dn) -{ - 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); - } - - HRESULT hr = CoInitialize(nullptr); - if (hr != S_OK) return; - CoInitializeSecurity(NULL, -1, NULL, NULL, - RPC_C_AUTHN_LEVEL_PKT, - RPC_C_IMP_LEVEL_IMPERSONATE, - NULL, EOAC_NONE, NULL); - if (hr != S_OK) return; - - INetSharingManager *nsm; - //hr = CoCreateInstance(__uuidof(NetSharingManager), NULL, CLSCTX_ALL, __uuidof(INetSharingManager), (void**)&nsm); - hr = CoCreateInstance(CLSID_NetSharingManager, NULL, CLSCTX_ALL, IID_INetSharingManager, (void**)&nsm); - if (hr != S_OK) return; - - bool found = false; - INetSharingEveryConnectionCollection *nsecc = nullptr; - hr = nsm->get_EnumEveryConnection(&nsecc); - if (!nsecc) { - fprintf(stderr, "Failed to get NSM connections"); - return; - } - - IEnumVARIANT *ev = nullptr; - IUnknown *unk = nullptr; - hr = nsecc->get__NewEnum(&unk); - if (unk) { - //hr = unk->QueryInterface(__uuidof(IEnumVARIANT), (void**)&ev); - hr = unk->QueryInterface(IID_IEnumVARIANT, (void**)&ev); - unk->Release(); - } - if (ev) { - VARIANT v; - VariantInit(&v); - - while ((S_OK == ev->Next(1, &v, NULL)) && found == FALSE) { - if (V_VT(&v) == VT_UNKNOWN) { - INetConnection *nc = nullptr; - //V_UNKNOWN(&v)->QueryInterface(__uuidof(INetConnection), (void**)&nc); - V_UNKNOWN(&v)->QueryInterface(IID_INetConnection, (void**)&nc); - if (nc) { - NETCON_PROPERTIES *ncp = nullptr; - nc->GetProperties(&ncp); - - GUID curId = ncp->guidId; - if (curId == _deviceGuid) { - wchar_t wtext[255]; - mbstowcs(wtext, dn, strlen(dn)+1); - nc->Rename(wtext); - found = true; - } - nc->Release(); - } - } - VariantClear(&v); - } - ev->Release(); - } - nsecc->Release(); -} - -void WindowsEthernetTap::scanMulticastGroups(std::vector &added,std::vector &removed) -{ - if (!_initialized) - return; - HANDLE t = _tap; - if (t == INVALID_HANDLE_VALUE) - return; - - std::vector 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 - // 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; - if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)mcastbuf,sizeof(mcastbuf),(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) { - if ((bytesReturned > 0)&&(bytesReturned <= TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE)) { // sanity check - MAC mac; - DWORD i = 0; - while ((i + 6) <= bytesReturned) { - mac.setTo((uint8_t *)(mcastbuf + i)); - i += 6; - if ((mac.isMulticast())&&(!mac.isBroadcast())) { - // exclude the nulls that may be returned or any other junk Windows puts in there - newGroups.push_back(MulticastGroup(mac,0)); - } - } - } - } - - std::vector allIps(ips()); - for(std::vector::iterator ip(allIps.begin());ip!=allIps.end();++ip) - newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); - - std::sort(newGroups.begin(),newGroups.end()); - newGroups.erase(std::unique(newGroups.begin(),newGroups.end()),newGroups.end()); - - for(std::vector::iterator m(newGroups.begin());m!=newGroups.end();++m) { - if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) - added.push_back(*m); - } - for(std::vector::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { - if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) - removed.push_back(*m); - } - - _multicastGroups.swap(newGroups); -} - -void WindowsEthernetTap::setMtu(unsigned int mtu) -{ - if (mtu != _mtu) { - _mtu = mtu; - HKEY nwAdapters; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ | KEY_WRITE, &nwAdapters) == ERROR_SUCCESS) { - char tmps[64]; - unsigned int tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu); - RegSetKeyValueA(nwAdapters, _mySubkeyName.c_str(), "MTU", REG_SZ, tmps, tmpsl); - RegCloseKey(nwAdapters); - } - } -} - -NET_IFINDEX WindowsEthernetTap::interfaceIndex() const -{ - NET_IFINDEX idx = -1; - if (ConvertInterfaceLuidToIndex(&_deviceLuid,&idx) == NO_ERROR) - return idx; - return -1; -} - -void WindowsEthernetTap::threadMain() - throw() -{ - char tapReadBuf[ZT_MAX_MTU + 32]; - char tapPath[128]; - HANDLE wait4[3]; - OVERLAPPED tapOvlRead,tapOvlWrite; - - OSUtils::ztsnprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); - - try { - while (_run) { - // Because Windows - Sleep(250); - setPersistentTapDeviceState(_deviceInstanceId.c_str(),false); - Sleep(250); - setPersistentTapDeviceState(_deviceInstanceId.c_str(),true); - Sleep(250); - setPersistentTapDeviceState(_deviceInstanceId.c_str(),false); - Sleep(250); - setPersistentTapDeviceState(_deviceInstanceId.c_str(),true); - Sleep(250); - - _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); - if (_tap == INVALID_HANDLE_VALUE) { - Sleep(250); - continue; - } - - { - uint32_t tmpi = 1; - DWORD bytesReturned = 0; - DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); - } - -#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. - * - * 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(250); - 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 = (NL_ROUTE_PROTOCOL)MIB_IPPROTO_NETMGMT; - DWORD result = CreateIpForwardEntry2(&nr); - if (result != NO_ERROR) - Sleep(250); - else break; - } - } -#endif - - // Assign or re-assign any should-be-assigned IPs in case we have restarted - { - Mutex::Lock _l(_assignedIps_m); - _syncIps(); - } - - 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(); - _initialized = true; - unsigned int oldmtu = _mtu; - - setFriendlyName(_friendlyName.c_str()); - - while (_run) { - DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE); - if (!_run) break; // will also break outer while(_run) since _run is false - - // Check for changes in MTU and break to restart tap device to reconfigure in this case - if (_mtu != oldmtu) - break; - - // 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; - 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(aabuf),&aalen) == NO_ERROR) { - bool isBorked = false; - - PIP_ADAPTER_ADDRESSES aa = reinterpret_cast(aabuf); - while (aa) { - if (_deviceLuid.Value == aa->Luid.Value) { - isBorked = (aa->OperStatus != IfOperStatusUp); - break; - } - aa = aa->Next; - } - - if (isBorked) { - // Close and reopen tap device if there's an issue (outer loop) - break; - } - } - } - } - - if ((waitResult == WAIT_TIMEOUT)||(waitResult == WAIT_FAILED)) { - Sleep(250); // guard against spinning under some conditions - continue; - } - - if (HasOverlappedIoCompleted(&tapOvlRead)) { - DWORD bytesRead = 0; - if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) { - if ((bytesRead > 14)&&(_enabled)) { - MAC to((uint8_t *)tapReadBuf); - MAC from((uint8_t *)(tapReadBuf + 6)); - unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xffU) << 8U) | (((unsigned int)tapReadBuf[13]) & 0xffU); - try { - _handler(_arg,nullptr,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14); - } catch ( ... ) {} // handlers should not throw - } - } - ReadFile(_tap,tapReadBuf,ZT_MAX_MTU + 32,NULL,&tapOvlRead); - } - - 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().data,_injectPending.front().len,NULL,&tapOvlWrite); - writeInProgress = true; - } - - _injectPending_m.unlock(); - } - - CancelIo(_tap); - - CloseHandle(tapOvlRead.hEvent); - CloseHandle(tapOvlWrite.hEvent); - CloseHandle(_tap); - _tap = INVALID_HANDLE_VALUE; - - // 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 -} - -NET_IFINDEX WindowsEthernetTap::_getDeviceIndex() -{ - MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0; - - if (GetIfTable2Ex(MibIfTableRaw,&ift) != NO_ERROR) - throw std::runtime_error("GetIfTable2Ex() failed"); - - if (ift->NumEntries > 0) { - for(ULONG i=0;iNumEntries;++i) { - if (ift->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - NET_IFINDEX idx = ift->Table[i].InterfaceIndex; - FreeMibTable(ift); - return idx; - } - } - } - - FreeMibTable(&ift); - - 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); - } -} - -void WindowsEthernetTap::_syncIps() -{ - // assumes _assignedIps_m is locked - - if (!_initialized) - return; - - std::vector haveIps(ips()); - - for(std::vector::const_iterator aip(_assignedIps.begin());aip!=_assignedIps.end();++aip) { - if (std::find(haveIps.begin(),haveIps.end(),*aip) == haveIps.end()) { - MIB_UNICASTIPADDRESS_ROW ipr; - - InitializeUnicastIpAddressEntry(&ipr); - if (aip->isV4()) { - ipr.Address.Ipv4.sin_family = AF_INET; - ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)aip->rawIpData()); - ipr.OnLinkPrefixLength = aip->netmaskBits(); - if (ipr.OnLinkPrefixLength >= 32) - continue; - } else if (aip->isV6()) { - ipr.Address.Ipv6.sin6_family = AF_INET6; - memcpy(ipr.Address.Ipv6.sin6_addr.u.Byte,aip->rawIpData(),16); - ipr.OnLinkPrefixLength = aip->netmaskBits(); - if (ipr.OnLinkPrefixLength >= 128) - continue; - } else continue; - - ipr.PrefixOrigin = IpPrefixOriginManual; - ipr.SuffixOrigin = IpSuffixOriginManual; - ipr.ValidLifetime = 0xffffffff; - ipr.PreferredLifetime = 0xffffffff; - - ipr.InterfaceLuid = _deviceLuid; - ipr.InterfaceIndex = _getDeviceIndex(); - - CreateUnicastIpAddressEntry(&ipr); - } - - if (aip->isV4()) { - char ipbuf[64]; - std::string ipStr(aip->toIpString(ipbuf)); - std::vector regIps(_getRegistryIPv4Value("IPAddress")); - if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) { - std::vector regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); - regIps.push_back(ipStr); - regSubnetMasks.push_back(aip->netmask().toIpString(ipbuf)); - _setRegistryIPv4Value("IPAddress", regIps); - _setRegistryIPv4Value("SubnetMask", regSubnetMasks); - } - } - } -} - -} // namespace ZeroTier - -#endif diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp deleted file mode 100644 index f5fe562f6..000000000 --- a/osdep/WindowsEthernetTap.hpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2025-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_WINDOWSETHERNETTAP_HPP -#define ZT_WINDOWSETHERNETTAP_HPP - -#include "../core/Constants.hpp" - -#ifdef __WINDOWS__ - -#include -#include - -#include - -#include -#include -#include - -#include "../core/Mutex.hpp" -#include "../core/MulticastGroup.hpp" -#include "../core/InetAddress.hpp" -#include "../osdep/Thread.hpp" -#include "EthernetTap.hpp" - -namespace ZeroTier { - -class WindowsEthernetTap : public EthernetTap -{ -public: - /** - * Installs a new instance of the ZT tap driver - * - * @param pathToInf Path to zttap driver .inf file - * @param deviceInstanceId Buffer to fill with device instance ID on success (and if SetupDiGetDeviceInstanceIdA succeeds, which it should) - * @return Empty string on success, otherwise an error message - */ - static std::string addNewPersistentTapDevice(const char *pathToInf,std::string &deviceInstanceId); - - /** - * Uninstalls all persistent tap devices that have legacy drivers - * - * @return Empty string on success, otherwise an error message - */ - static std::string destroyAllLegacyPersistentTapDevices(); - - /** - * Uninstalls all persistent tap devices on the system - * - * @return Empty string on success, otherwise an error message - */ - static std::string destroyAllPersistentTapDevices(); - - /** - * Uninstalls a specific persistent tap device by instance ID - * - * @param instanceId Device instance ID - * @return Empty string on success, otherwise an error message - */ - static std::string deletePersistentTapDevice(const char *instanceId); - - /** - * Disable a persistent tap device by instance ID - * - * @param instanceId Device instance ID - * @param enabled Enable device? - * @return True if device was found and disabled - */ - static bool setPersistentTapDeviceState(const char *instanceId,bool enabled); - - WindowsEthernetTap( - const char *hp, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *friendlyName, - void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), - void *arg); - - virtual ~WindowsEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIp(const InetAddress &ip); - virtual bool removeIp(const InetAddress &ip); - virtual std::vector ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual void scanMulticastGroups(std::vector &added,std::vector &removed); - virtual void setMtu(unsigned int mtu); - - inline const NET_LUID &luid() const { return _deviceLuid; } - inline const GUID &guid() const { return _deviceGuid; } - inline const std::string &instanceId() const { return _deviceInstanceId; } - NET_IFINDEX interfaceIndex() const; - - void threadMain() - throw(); - - bool isInitialized() const { return _initialized; }; - -private: - NET_IFINDEX _getDeviceIndex(); // throws on failure - std::vector _getRegistryIPv4Value(const char *regKey); - void _setRegistryIPv4Value(const char *regKey,const std::vector &value); - void _syncIps(); - - void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); - void *_arg; - MAC _mac; - uint64_t _nwid; - volatile unsigned int _mtu; - Thread _thread; - - volatile HANDLE _tap; - HANDLE _injectSemaphore; - - GUID _deviceGuid; - NET_LUID _deviceLuid; - std::string _netCfgInstanceId; - std::string _deviceInstanceId; - std::string _mySubkeyName; - - std::string _friendlyName; - - std::vector _assignedIps; // IPs assigned with addIp - Mutex _assignedIps_m; - - std::vector _multicastGroups; - - struct _InjectPending - { - unsigned int len; - char data[ZT_MAX_MTU + 32]; - }; - std::queue<_InjectPending> _injectPending; - Mutex _injectPending_m; - - std::string _pathToHelpers; - - volatile bool _run; - volatile bool _initialized; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif - -#endif diff --git a/osdep/freebsd_getifmaddrs.c b/osdep/freebsd_getifmaddrs.c deleted file mode 100644 index 8b1741466..000000000 --- a/osdep/freebsd_getifmaddrs.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - this BSD licensed code is from https://github.com/freebsd/freebsd/blob/386ddae58459341ec567604707805814a2128a57/lib/libc/net/getifmaddrs.c - as in older OS X there is no getifmaddrs() and related functions is NetBSD -*/ - -#define NET_RT_IFMALIST 4 /* return multicast address list */ -#define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ - - -/* - * Copyright (c) 2003 Bruce M. Simpson. - * All rights reserved - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -/* -__FBSDID("$FreeBSD$"); - -#include "namespace.h"*/ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -//#include "un-namespace.h" - -#define SALIGN (sizeof(long) - 1) -#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ - (SALIGN + 1)) -#define MAX_SYSCTL_TRY 5 -#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) - -int -getifmaddrs(struct ifmaddrs **pif) -{ - int icnt = 1; - int dcnt = 0; - int ntry = 0; - size_t len; - size_t needed; - int mib[6]; - int i; - char *buf; - char *data; - char *next; - char *p; - struct ifma_msghdr *ifmam; - struct ifmaddrs *ifa, *ift; - struct rt_msghdr *rtm; - struct sockaddr *sa; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; /* protocol */ - mib[3] = 0; /* wildcard address family */ - mib[4] = NET_RT_IFMALIST; - mib[5] = 0; /* no flags */ - do { - if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) - return (-1); - if ((buf = malloc(needed)) == NULL) - return (-1); - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { - if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { - free(buf); - return (-1); - } - free(buf); - buf = NULL; - } - } while (buf == NULL); - - for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)(void *)next; - if (rtm->rtm_version != RTM_VERSION) - continue; - switch (rtm->rtm_type) { - case RTM_NEWMADDR: - ifmam = (struct ifma_msghdr *)(void *)rtm; - if ((ifmam->ifmam_addrs & RTA_IFA) == 0) - break; - icnt++; - p = (char *)(ifmam + 1); - for (i = 0; i < RTAX_MAX; i++) { - if ((RTA_MASKS & ifmam->ifmam_addrs & - (1 << i)) == 0) - continue; - sa = (struct sockaddr *)(void *)p; - len = SA_RLEN(sa); - dcnt += len; - p += len; - } - break; - } - } - - data = malloc(sizeof(struct ifmaddrs) * icnt + dcnt); - if (data == NULL) { - free(buf); - return (-1); - } - - ifa = (struct ifmaddrs *)(void *)data; - data += sizeof(struct ifmaddrs) * icnt; - - memset(ifa, 0, sizeof(struct ifmaddrs) * icnt); - ift = ifa; - - for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)(void *)next; - if (rtm->rtm_version != RTM_VERSION) - continue; - - switch (rtm->rtm_type) { - case RTM_NEWMADDR: - ifmam = (struct ifma_msghdr *)(void *)rtm; - if ((ifmam->ifmam_addrs & RTA_IFA) == 0) - break; - - p = (char *)(ifmam + 1); - for (i = 0; i < RTAX_MAX; i++) { - if ((RTA_MASKS & ifmam->ifmam_addrs & - (1 << i)) == 0) - continue; - sa = (struct sockaddr *)(void *)p; - len = SA_RLEN(sa); - switch (i) { - case RTAX_GATEWAY: - ift->ifma_lladdr = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - case RTAX_IFP: - ift->ifma_name = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - case RTAX_IFA: - ift->ifma_addr = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - default: - data += len; - break; - } - p += len; - } - ift->ifma_next = ift + 1; - ift = ift->ifma_next; - break; - } - } - - free(buf); - - if (ift > ifa) { - ift--; - ift->ifma_next = NULL; - *pif = ifa; - } else { - *pif = NULL; - free(ifa); - } - return (0); -} - -void -freeifmaddrs(struct ifmaddrs *ifmp) -{ - - free(ifmp); -} diff --git a/osdep/freebsd_getifmaddrs.h b/osdep/freebsd_getifmaddrs.h deleted file mode 100644 index 963c422d3..000000000 --- a/osdep/freebsd_getifmaddrs.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - this BSD code is from https://github.com/freebsd/freebsd/blob/386ddae58459341ec567604707805814a2128a57/include/ifaddrs.h - as in older OS X there is no getifmaddrs() and related functions is NetBSD -*/ - -/* $FreeBSD$ */ - -/* - * Copyright (c) 1995, 1999 - * Berkeley Software Design, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp - */ - -#ifndef _freebsd_getifmaddrs.h_ -#define _freebsd_getifmaddrs.h_ - -/* - * This may have been defined in . Note that if is - * to be included it must be included before this header file. - */ -#ifndef ifa_broadaddr -#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ -#endif - -struct ifmaddrs { - struct ifmaddrs *ifma_next; - struct sockaddr *ifma_name; - struct sockaddr *ifma_addr; - struct sockaddr *ifma_lladdr; -}; - -#include - - -/* - * Message format for use in obtaining information about multicast addresses - * from the routing socket. - */ -struct ifma_msghdr { - int ifmam_msglen; /* to skip over non-understood messages */ - int ifmam_version; /* future binary compatibility */ - int ifmam_type; /* message type */ - int ifmam_addrs; /* like rtm_addrs */ - int ifmam_flags; /* value of ifa_flags */ - int ifmam_index; /* index for associated ifp */ -}; - - -extern int getifaddrs(struct ifaddrs **); -extern void freeifaddrs(struct ifaddrs *); -extern int getifmaddrs(struct ifmaddrs **); -extern void freeifmaddrs(struct ifmaddrs *); -#include "freebsd_getifmaddrs.c" - - -#endif diff --git a/osdep/rust-osdep.cpp b/osdep/rust-osdep.cpp index 600141580..96c2943a3 100644 --- a/osdep/rust-osdep.cpp +++ b/osdep/rust-osdep.cpp @@ -1,3 +1,12 @@ +/* Fix for an issue with this structure not being present on MacOS */ +#ifdef __APPLE__ +struct prf_ra { + unsigned char onlink : 1; + unsigned char autonomous : 1; + unsigned char reserved : 6; +} prf_ra; +#endif + #include "../core/Constants.hpp" #include "../core/Mutex.hpp" #include "../core/Containers.hpp" @@ -104,4 +113,7 @@ const char *platformDefaultHomePath() int64_t msSinceEpoch() { return ZeroTier::OSUtils::now(); } +void lockDownFile(const char *path, int isDir) +{ ZeroTier::OSUtils::lockDownFile(path, isDir != 0); } + } diff --git a/osdep/rust-osdep.h b/osdep/rust-osdep.h index e076b108e..c0722acd4 100644 --- a/osdep/rust-osdep.h +++ b/osdep/rust-osdep.h @@ -5,11 +5,15 @@ #ifdef __APPLE__ #include #include -#include +#include +#include #include #include #include +#include +#include #include +#include #include #include #include @@ -53,6 +57,7 @@ extern "C" { extern const char *platformDefaultHomePath(); extern int64_t msSinceEpoch(); +extern void lockDownFile(const char *path, int isDir); #ifdef __cplusplus } diff --git a/rust-zerotier-core/src/node.rs b/rust-zerotier-core/src/node.rs index 95dbeda9b..b9c0c0724 100644 --- a/rust-zerotier-core/src/node.rs +++ b/rust-zerotier-core/src/node.rs @@ -13,7 +13,6 @@ use std::cell::Cell; use std::collections::hash_map::HashMap; -use std::ffi::CStr; use std::intrinsics::copy_nonoverlapping; use std::mem::{MaybeUninit, transmute}; use std::os::raw::{c_int, c_uint, c_ulong, c_void}; @@ -28,7 +27,8 @@ use serde::{Deserialize, Serialize}; use crate::*; use crate::capi as ztcore; -const NODE_BACKGROUND_MAX_DELAY: i64 = 500; +/// Maximum delay between calls to run_background_tasks() +pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: u64 = 250; #[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] pub enum Event { @@ -77,10 +77,10 @@ pub trait NodeEventHandler { fn event(&self, event: Event, event_data: &[u8]); /// Called to store an object into the object store. - fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]); + fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()>; /// Called to retrieve an object from the object store. - fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option>; + fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result>; /// Called to send a packet over the physical network (virtual -> physical). fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32; @@ -95,11 +95,10 @@ pub trait NodeEventHandler { /// An instance of the ZeroTier core. /// This is templated on the actual implementation of NodeEventHandler for performance reasons, /// as it avoids an extra indirect function call. -pub struct Node + 'static, N: 'static> { - event_handler: Arc, - capi: Cell<*mut ztcore::ZT_Node>, - background_thread: Cell>>, - background_thread_run: Arc, +#[allow(non_snake_case)] +pub struct Node + Sync + Send + Clone + 'static, N: 'static> { + event_handler: T, + capi: *mut ztcore::ZT_Node, now: PortableAtomicI64, networks_by_id: Mutex>> // pointer to an Arc<> is a raw value created from Box> } @@ -116,7 +115,7 @@ macro_rules! node_from_raw_ptr { } } -extern "C" fn zt_virtual_network_config_function + 'static, N: 'static>( +extern "C" fn zt_virtual_network_config_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -139,7 +138,7 @@ extern "C" fn zt_virtual_network_config_function + 'stati } } -extern "C" fn zt_virtual_network_frame_function + 'static, N: 'static>( +extern "C" fn zt_virtual_network_frame_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -165,7 +164,7 @@ extern "C" fn zt_virtual_network_frame_function + 'static } } -extern "C" fn zt_event_callback + 'static, N: 'static>( +extern "C" fn zt_event_callback + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -186,7 +185,7 @@ extern "C" fn zt_event_callback + 'static, N: 'static>( } } -extern "C" fn zt_state_put_function + 'static, N: 'static>( +extern "C" fn zt_state_put_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -202,11 +201,11 @@ extern "C" fn zt_state_put_function + 'static, N: 'static let n = node_from_raw_ptr!(uptr); let obj_id2 = unsafe { &*slice_from_raw_parts(obj_id, obj_id_len as usize) }; let obj_data2 = unsafe { &*slice_from_raw_parts(obj_data.cast::(), obj_data_len as usize) }; - n.event_handler.state_put(obj_type2, obj_id2, obj_data2); + let _ = n.event_handler.state_put(obj_type2, obj_id2, obj_data2); } } -extern "C" fn zt_state_get_function + 'static, N: 'static>( +extern "C" fn zt_state_get_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -230,7 +229,7 @@ extern "C" fn zt_state_get_function + 'static, N: 'static let n = node_from_raw_ptr!(uptr); let obj_id2 = unsafe { &*slice_from_raw_parts(obj_id, obj_id_len as usize) }; let obj_data_result = n.event_handler.state_get(obj_type2, obj_id2); - if obj_data_result.is_some() { + if obj_data_result.is_ok() { let obj_data_result = obj_data_result.unwrap(); if obj_data_result.len() > 0 { unsafe { @@ -248,7 +247,7 @@ extern "C" fn zt_state_get_function + 'static, N: 'static return -1; } -extern "C" fn zt_wire_packet_send_function + 'static, N: 'static>( +extern "C" fn zt_wire_packet_send_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -262,7 +261,7 @@ extern "C" fn zt_wire_packet_send_function + 'static, N: return n.event_handler.wire_packet_send(local_socket, InetAddress::transmute_capi(unsafe { &*sock_addr }), unsafe { &*slice_from_raw_parts(data.cast::(), data_size as usize) }, packet_ttl as u32) as c_int; } -extern "C" fn zt_path_check_function + 'static, N: 'static>( +extern "C" fn zt_path_check_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -279,7 +278,7 @@ extern "C" fn zt_path_check_function + 'static, N: 'stati return 0; } -extern "C" fn zt_path_lookup_function + 'static, N: 'static>( +extern "C" fn zt_path_lookup_function + Sync + Send + Clone + 'static, N: 'static>( _: *mut ztcore::ZT_Node, uptr: *mut c_void, _: *mut c_void, @@ -318,19 +317,17 @@ extern "C" fn zt_path_lookup_function + 'static, N: 'stat ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -impl + 'static, N: 'static> Node { +impl + Sync + Send + Clone + 'static, N: 'static> Node { /// Create a new Node with a given event handler. - pub fn new(event_handler: Arc) -> Result>, ResultCode> { + pub fn new(event_handler: T) -> Result, ResultCode> { let now = now(); - let n = Arc::new(Node { + let mut n = Node { event_handler: event_handler.clone(), - capi: Cell::new(null_mut()), - background_thread: Cell::new(None), - background_thread_run: Arc::new(AtomicBool::new(true)), + capi: null_mut(), now: PortableAtomicI64::new(now), networks_by_id: Mutex::new(HashMap::new()) - }); + }; let mut capi: *mut ztcore::ZT_Node = null_mut(); unsafe { @@ -344,55 +341,47 @@ impl + 'static, N: 'static> Node { pathCheckFunction: transmute(zt_path_check_function:: as *const ()), pathLookupFunction: transmute(zt_path_lookup_function:: as *const ()), }; - - let rc = ztcore::ZT_Node_new(&mut capi as *mut *mut ztcore::ZT_Node, transmute(Arc::as_ptr(&n)), null_mut(), &callbacks as *const ztcore::ZT_Node_Callbacks, now); + let rc = ztcore::ZT_Node_new(&mut capi as *mut *mut ztcore::ZT_Node, transmute(&n as *const Node), null_mut(), &callbacks as *const ztcore::ZT_Node_Callbacks, now); if rc != 0 { return Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal)); } else if capi.is_null() { return Err(ResultCode::FatalErrorInternal); } } - n.capi.replace(capi); - - let wn = Arc::downgrade(&n); - let run = n.background_thread_run.clone(); - n.background_thread.replace(Some(std::thread::Builder::new().stack_size(RECOMMENDED_THREAD_STACK_SIZE).spawn(move || { - let mut loop_delay = Duration::from_millis(NODE_BACKGROUND_MAX_DELAY as u64); - while run.load(Ordering::Relaxed) { - std::thread::park_timeout(loop_delay); - if run.load(Ordering::Relaxed) { - let nn = wn.upgrade(); - if nn.is_some() { - loop_delay = Duration::from_millis(nn.unwrap().process_background_tasks() as u64); - } else { - break; - } - } else { - break; - } - } - }).unwrap())); + n.capi = capi; Ok(n) } - /// This is called periodically from the background service thread. It's not called from anywhere else. - fn process_background_tasks(&self) -> i64 { + /// Perform periodic background tasks. + /// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds + /// since the node was created, and after this runs it returns the amount of time the caller + /// should wait before calling it again. + pub fn process_background_tasks(&self) -> u64 { let current_time = now(); self.now.set(current_time); let mut next_task_deadline: i64 = current_time; unsafe { - ztcore::ZT_Node_processBackgroundTasks(self.capi.get(), null_mut(), current_time, &mut next_task_deadline as *mut i64); + ztcore::ZT_Node_processBackgroundTasks(self.capi, null_mut(), current_time, (&mut next_task_deadline as *mut i64).cast()); } let mut next_delay = next_task_deadline - current_time; - if next_delay < 10 { - next_delay = 10; - } else if next_delay > NODE_BACKGROUND_MAX_DELAY { - next_delay = NODE_BACKGROUND_MAX_DELAY; + if next_delay < 1 { + next_delay = 1; + } else if next_delay > NODE_BACKGROUND_TASKS_MAX_INTERVAL as i64 { + next_delay = NODE_BACKGROUND_TASKS_MAX_INTERVAL as i64; + } + next_delay as u64 + } + + fn delete_network_uptr(&self, nwid: u64) { + let nptr = self.networks_by_id.lock().unwrap().remove(&nwid).unwrap_or(null_mut()); + if !nptr.is_null() { + unsafe { + Box::from_raw(nptr); + } } - next_delay } pub fn join(&self, nwid: NetworkId, controller_fingerprint: Option, network_obj: &Arc) -> ResultCode { @@ -409,33 +398,24 @@ impl + 'static, N: 'static> Node { let nptr = Box::into_raw(Box::new(network_obj.clone())); self.networks_by_id.lock().as_deref_mut().unwrap().insert(nwid.0, nptr); - let rc = unsafe { ztcore::ZT_Node_join(self.capi.get(), nwid.0, cfpp, nptr.cast::(), null_mut()) }; + let rc = unsafe { ztcore::ZT_Node_join(self.capi, nwid.0, cfpp, nptr.cast::(), null_mut()) }; if rc != ztcore::ZT_ResultCode_ZT_RESULT_OK { self.delete_network_uptr(nwid.0); } return ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); } - fn delete_network_uptr(&self, nwid: u64) { - let nptr = self.networks_by_id.lock().as_deref_mut().unwrap().remove(&nwid).unwrap_or(null_mut()); - if !nptr.is_null() { - unsafe { - Box::from_raw(nptr); - } - } - } - pub fn leave(&self, nwid: NetworkId) -> ResultCode { self.delete_network_uptr(nwid.0); unsafe { - return ResultCode::from_i32(ztcore::ZT_Node_leave(self.capi.get(), nwid.0, null_mut(), null_mut()) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); + return ResultCode::from_i32(ztcore::ZT_Node_leave(self.capi, nwid.0, null_mut(), null_mut()) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); } } #[inline(always)] pub fn address(&self) -> Address { unsafe { - return Address(ztcore::ZT_Node_address(self.capi.get()) as u64); + return Address(ztcore::ZT_Node_address(self.capi) as u64); } } @@ -443,7 +423,7 @@ impl + 'static, N: 'static> Node { pub fn process_wire_packet(&self, local_socket: i64, remote_address: &InetAddress, data: Buffer) -> ResultCode { let current_time = self.now.get(); let mut next_task_deadline: i64 = current_time; - let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processWirePacket(self.capi.get(), null_mut(), current_time, local_socket, remote_address.as_capi_ptr(), data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_task_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }; + let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processWirePacket(self.capi, null_mut(), current_time, local_socket, remote_address.as_capi_ptr(), data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_task_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }; std::mem::forget(data); // prevent Buffer from being returned to ZT core twice, see comment in drop() in buffer.rs rc } @@ -452,7 +432,7 @@ impl + 'static, N: 'static> Node { pub fn process_virtual_network_frame(&self, nwid: &NetworkId, source_mac: &MAC, dest_mac: &MAC, ethertype: u16, vlan_id: u16, data: Buffer) -> ResultCode { let current_time = self.now.get(); let mut next_tick_deadline: i64 = current_time; - let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processVirtualNetworkFrame(self.capi.get(), null_mut(), current_time, nwid.0, source_mac.0, dest_mac.0, ethertype as c_uint, vlan_id as c_uint, data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_tick_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }; + let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processVirtualNetworkFrame(self.capi, null_mut(), current_time, nwid.0, source_mac.0, dest_mac.0, ethertype as c_uint, vlan_id as c_uint, data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_tick_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) }; std::mem::forget(data); // prevent Buffer from being returned to ZT core twice, see comment in drop() in buffer.rs rc } @@ -460,14 +440,14 @@ impl + 'static, N: 'static> Node { #[inline(always)] pub fn multicast_subscribe(&self, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode { unsafe { - return ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.capi.get(), null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); + return ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.capi, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); } } #[inline(always)] pub fn multicast_unsubscribe(&self, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode { unsafe { - return ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.capi.get(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); + return ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.capi, nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal); } } @@ -482,13 +462,13 @@ impl + 'static, N: 'static> Node { /// to exist the Identity becomes invalid. Use clone() on it to get a copy. #[inline(always)] pub(crate) unsafe fn identity_fast(&self) -> Identity { - Identity::new_from_capi(ztcore::ZT_Node_identity(self.capi.get()), false) + Identity::new_from_capi(ztcore::ZT_Node_identity(self.capi), false) } pub fn status(&self) -> NodeStatus { let mut ns: MaybeUninit = MaybeUninit::zeroed(); unsafe { - ztcore::ZT_Node_status(self.capi.get(), ns.as_mut_ptr()); + ztcore::ZT_Node_status(self.capi, ns.as_mut_ptr()); let ns = ns.assume_init(); if ns.identity.is_null() { panic!("ZT_Node_status() returned null identity"); @@ -496,8 +476,8 @@ impl + 'static, N: 'static> Node { return NodeStatus { address: Address(ns.address), identity: Identity::new_from_capi(&*ns.identity, false).clone(), - public_identity: String::from(CStr::from_ptr(ns.publicIdentity).to_str().unwrap()), - secret_identity: String::from(CStr::from_ptr(ns.secretIdentity).to_str().unwrap()), + public_identity: cstr_to_string(ns.publicIdentity, -1), + secret_identity: cstr_to_string(ns.secretIdentity, -1), online: ns.online != 0, }; } @@ -506,7 +486,7 @@ impl + 'static, N: 'static> Node { pub fn peers(&self) -> Vec { let mut p: Vec = Vec::new(); unsafe { - let pl = ztcore::ZT_Node_peers(self.capi.get()); + let pl = ztcore::ZT_Node_peers(self.capi); if !pl.is_null() { let peer_count = (*pl).peerCount as usize; p.reserve(peer_count); @@ -522,7 +502,7 @@ impl + 'static, N: 'static> Node { pub fn networks(&self) -> Vec { let mut n: Vec = Vec::new(); unsafe { - let nl = ztcore::ZT_Node_networks(self.capi.get()); + let nl = ztcore::ZT_Node_networks(self.capi); if !nl.is_null() { let net_count = (*nl).networkCount as usize; n.reserve(net_count); @@ -538,7 +518,7 @@ impl + 'static, N: 'static> Node { pub fn certificates(&self) -> Vec<(Certificate, u32)> { let mut c: Vec<(Certificate, u32)> = Vec::new(); unsafe { - let cl = ztcore::ZT_Node_listCertificates(self.capi.get()); + let cl = ztcore::ZT_Node_listCertificates(self.capi); if !cl.is_null() { let cert_count = (*cl).certCount as usize; c.reserve(cert_count); @@ -552,20 +532,12 @@ impl + 'static, N: 'static> Node { } } -unsafe impl + 'static, N: 'static> Sync for Node {} +unsafe impl + Sync + Send + Clone + 'static, N: 'static> Sync for Node {} -unsafe impl + 'static, N: 'static> Send for Node {} +unsafe impl + Sync + Send + Clone + 'static, N: 'static> Send for Node {} -impl + 'static,N: 'static> Drop for Node { +impl + Sync + Send + Clone + 'static,N: 'static> Drop for Node { fn drop(&mut self) { - self.background_thread_run.store(false, Ordering::Relaxed); - let bt = self.background_thread.replace(None); - if bt.is_some() { - let bt = bt.unwrap(); - bt.thread().unpark(); - let _ = bt.join(); - } - // Manually take care of the unboxed Boxes in networks_by_id let mut nwids: Vec = Vec::new(); for n in self.networks_by_id.lock().unwrap().iter() { @@ -576,7 +548,7 @@ impl + 'static,N: 'static> Drop for Node { } unsafe { - ztcore::ZT_Node_delete(self.capi.get(), null_mut()); + ztcore::ZT_Node_delete(self.capi, null_mut()); } } } diff --git a/rust-zerotier-service/src/commands/service.rs b/rust-zerotier-service/src/commands/service.rs index 1389e8326..49ad55ba7 100644 --- a/rust-zerotier-service/src/commands/service.rs +++ b/rust-zerotier-service/src/commands/service.rs @@ -11,6 +11,7 @@ */ /****/ +use std::cell::Cell; use std::collections::BTreeMap; use std::net::IpAddr; use std::rc::Rc; @@ -24,17 +25,32 @@ use warp::{Filter, Rejection, Reply}; use warp::http::{HeaderMap, Method, StatusCode}; use warp::hyper::body::Bytes; -use zerotier_core::*; +use zerotier_core::{Buffer, Address, IpScope, Node, NodeEventHandler, NetworkId, VirtualNetworkConfigOperation, VirtualNetworkConfig, StateObjectType, MAC, Event, InetAddress, InetAddressFamily, Identity}; use crate::fastudpsocket::*; use crate::getifaddrs; use crate::localconfig::*; use crate::log::Log; use crate::network::Network; +use crate::store::Store; + +// Check local addresses and bindings every (this) milliseconds. +const BINDING_CHECK_INTERVAL: i64 = 5000; struct Service { - local_config: Mutex, - run: AtomicBool, + local_config: Mutex>, + run: Arc, + store: Arc, +} + +impl Clone for Service { + fn clone(&self) -> Self { + Service { + local_config: Mutex::new(self.local_config.lock().unwrap().clone()), + run: self.run.clone(), + store: self.store.clone(), + } + } } impl NodeEventHandler for Service { @@ -46,13 +62,24 @@ impl NodeEventHandler for Service { } fn event(&self, event: Event, event_data: &[u8]) { + match event { + Event::Up => {}, + Event::Down => {}, + Event::Online => {}, + Event::Offline => {}, + Event::Trace => {}, + Event::UserMessage => {}, + } } - fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) { + #[inline(always)] + fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> { + self.store.store_object(&obj_type, obj_id, obj_data) } - fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option> { - None + #[inline(always)] + fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result> { + self.store.load_object(&obj_type, obj_id) } #[inline(always)] @@ -65,7 +92,18 @@ impl NodeEventHandler for Service { } fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option { - None + let lc = self.get_local_config(); + let vc = lc.virtual_.get(&address); + vc.map_or(None, |c: &LocalConfigVirtualConfig| { + if c.try_.is_empty() { + None + } else { + let t = c.try_.get((zerotier_core::random() as usize) % c.try_.len()); + t.map_or(None, |v: &InetAddress| { + Some(v.clone()) + }) + } + }) } } @@ -84,9 +122,20 @@ impl Service { fn web_api_peer(&self, peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes) -> Box { Box::new(warp::http::StatusCode::BAD_REQUEST) } + + #[inline(always)] + fn get_local_config(&self) -> Arc { + self.local_config.lock().unwrap().clone() + } + + #[inline(always)] + fn set_local_config(&self, new_lc: &Arc) { + let mut lc = self.local_config.lock().unwrap(); + *lc = new_lc.clone(); + } } -pub(crate) fn run() -> i32 { +pub(crate) fn run(store: &Arc) -> i32 { let mut process_exit_value: i32 = 0; let tokio_rt = tokio::runtime::Builder::new_current_thread().build().unwrap(); @@ -94,36 +143,49 @@ pub(crate) fn run() -> i32 { let mut udp_sockets: BTreeMap = BTreeMap::new(); let (mut interrupt_tx, mut interrupt_rx) = futures::channel::mpsc::channel::(2); - let service: Arc = Arc::new(Service { - local_config: Mutex::new(LocalConfig::default()), - run: AtomicBool::new(true), - }); + let service = Service { + local_config: Mutex::new(Arc::new(LocalConfig::default())), + run: Arc::new(AtomicBool::new(true)), + store: store.clone(), + }; + + let node = Node::new(service.clone()); + if node.is_err() { + // TODO: log and handle error + return; + } + let node = Arc::new(node.ok().unwrap()); let mut primary_port_bind_failure = false; + let mut last_checked_bindings: i64 = 0; + let mut loop_delay = zerotier_core::NODE_BACKGROUND_TASKS_MAX_INTERVAL; loop { - let current_local_config_settings = service.local_config.lock().unwrap().settings.clone(); + let mut current_local_config = service.get_local_config(); let (mut shutdown_tx, mut shutdown_rx) = futures::channel::oneshot::channel(); - let s0 = service.clone(); - let s1 = service.clone(); - let s2 = service.clone(); - let warp_server = warp::serve(warp::any().and(warp::path::end().map(|| { warp::reply::with_status("404", warp::hyper::StatusCode::NOT_FOUND) }) - .or(warp::path("status").and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes()) - .map(move |method: Method, headers: HeaderMap, post_data: Bytes| { s0.web_api_status(method, headers, post_data) })) - .or(warp::path!("network" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes()) - .map(move |network_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s1.web_api_network(network_str, method, headers, post_data) })) - .or(warp::path!("peer" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes()) - .map(move |peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s2.web_api_peer(peer_str, method, headers, post_data) })) - )).try_bind_with_graceful_shutdown((IpAddr::from([127_u8, 0_u8, 0_u8, 1_u8]), current_local_config_settings.primary_port), async { let _ = shutdown_rx.await; }); + let warp_server; + { + let s0 = service.clone(); + let s1 = service.clone(); + let s2 = service.clone(); + warp_server = warp::serve(warp::any().and(warp::path::end().map(|| { warp::reply::with_status("404", warp::hyper::StatusCode::NOT_FOUND) }) + .or(warp::path("status").and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes()) + .map(move |method: Method, headers: HeaderMap, post_data: Bytes| { s0.web_api_status(method, headers, post_data) })) + .or(warp::path!("network" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes()) + .map(move |network_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s1.web_api_network(network_str, method, headers, post_data) })) + .or(warp::path!("peer" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes()) + .map(move |peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s2.web_api_peer(peer_str, method, headers, post_data) })) + )).try_bind_with_graceful_shutdown((IpAddr::from([127_u8, 0_u8, 0_u8, 1_u8]), current_local_config.settings.primary_port), async { let _ = shutdown_rx.await; }); + } if warp_server.is_err() { primary_port_bind_failure = true; break; } + let warp_server = tokio_rt.spawn(warp_server.unwrap().1); - let mut loop_delay = 10; loop { tokio::select! { - _ = tokio::time::sleep(Duration::from_secs(loop_delay)) => {}, + _ = tokio::time::sleep(Duration::from_millis(loop_delay)) => {}, _ = interrupt_rx.next() => {}, _ = tokio::signal::ctrl_c() => { // TODO: log CTRL+C received @@ -133,64 +195,73 @@ pub(crate) fn run() -> i32 { } } - let mut system_addrs: BTreeMap = BTreeMap::new(); - getifaddrs::for_each_address(|addr: &InetAddress, dev: &str| { - match addr.ip_scope() { - IpScope::Global | IpScope::Private | IpScope::PseudoPrivate | IpScope::Shared => { - if !current_local_config_settings.is_interface_blacklisted(dev) { - let mut a = addr.clone(); - a.set_port(current_local_config_settings.primary_port); - system_addrs.insert(a, String::from(dev)); - if current_local_config_settings.secondary_port.is_some() { + loop_delay = node.process_background_tasks(); + + let now = zerotier_core::now(); + if (now - last_checked_bindings) >= BINDING_CHECK_INTERVAL { + last_checked_bindings = now; + + let mut system_addrs: BTreeMap = BTreeMap::new(); + getifaddrs::for_each_address(|addr: &InetAddress, dev: &str| { + match addr.ip_scope() { + IpScope::Global | IpScope::Private | IpScope::PseudoPrivate | IpScope::Shared => { + if !current_local_config.settings.is_interface_blacklisted(dev) { let mut a = addr.clone(); - a.set_port(current_local_config_settings.secondary_port.unwrap()); + a.set_port(current_local_config.settings.primary_port); system_addrs.insert(a, String::from(dev)); + if current_local_config.settings.secondary_port.is_some() { + let mut a = addr.clone(); + a.set_port(current_local_config.settings.secondary_port.unwrap()); + system_addrs.insert(a, String::from(dev)); + } } - } - }, - _ => {} - } - }); + }, + _ => {} + } + }); - let mut udp_sockets_to_close: Vec = Vec::new(); - for sock in udp_sockets.iter() { - if !system_addrs.contains_key(sock.0) { - udp_sockets_to_close.push(sock.0.clone()); - } - } - for k in udp_sockets_to_close.iter() { - udp_sockets.remove(k); - } - - for addr in system_addrs.iter() { - if !udp_sockets.contains_key(addr.0) { - let s = FastUDPSocket::new(addr.1.as_str(), addr.0, |raw_socket: &FastUDPRawOsSocket, from_address: &InetAddress, data: Buffer| { - // TODO: incoming packet handler - }); - if s.is_ok() { - udp_sockets.insert(addr.0.clone(), s.unwrap()); + let mut udp_sockets_to_close: Vec = Vec::new(); + for sock in udp_sockets.iter() { + if !system_addrs.contains_key(sock.0) { + udp_sockets_to_close.push(sock.0.clone()); } } - } + for k in udp_sockets_to_close.iter() { + udp_sockets.remove(k); + } - primary_port_bind_failure = true; - for s in udp_sockets.iter() { - if s.0.port() == current_local_config_settings.primary_port { - primary_port_bind_failure = false; + for addr in system_addrs.iter() { + if !udp_sockets.contains_key(addr.0) { + let s = FastUDPSocket::new(addr.1.as_str(), addr.0, |raw_socket: &FastUDPRawOsSocket, from_address: &InetAddress, data: Buffer| { + // TODO: incoming packet handler + }); + if s.is_ok() { + udp_sockets.insert(addr.0.clone(), s.unwrap()); + } + } + } + + primary_port_bind_failure = true; + for s in udp_sockets.iter() { + if s.0.port() == current_local_config.settings.primary_port { + primary_port_bind_failure = false; + break; + } + } + if primary_port_bind_failure { break; } } - if primary_port_bind_failure { - break; - } - if !service.run.load(Ordering::Relaxed) || current_local_config_settings.primary_port != service.local_config.lock().unwrap().settings.primary_port { + let next_local_config = service.get_local_config(); + if !service.run.load(Ordering::Relaxed) || current_local_config.settings.primary_port != next_local_config.settings.primary_port { let _ = shutdown_tx.send(()); break; } + current_local_config = next_local_config; } - let _ = warp_server.unwrap().1.await; + let _ = warp_server.await; if !service.run.load(Ordering::Relaxed) { break; @@ -201,7 +272,7 @@ pub(crate) fn run() -> i32 { } if primary_port_bind_failure { - let local_config = service.local_config.lock().unwrap(); + let local_config = service.get_local_config(); if local_config.settings.auto_port_search { // TODO: port hunting if enabled } diff --git a/rust-zerotier-service/src/fastudpsocket.rs b/rust-zerotier-service/src/fastudpsocket.rs index 8c5baef60..e038ffbaf 100644 --- a/rust-zerotier-service/src/fastudpsocket.rs +++ b/rust-zerotier-service/src/fastudpsocket.rs @@ -161,6 +161,19 @@ fn fast_udp_socket_close(socket: &FastUDPRawOsSocket) { } } +#[inline(always)] +pub(crate) fn fast_udp_socket_to_i64(socket: &FastUDPRawOsSocket) -> i64 { + (*socket) as i64 +} + +#[inline(always)] +pub(crate) fn fast_udp_socket_from_i64(socket: i64) -> Option { + if socket >= 0 { + return Some(socket as FastUDPRawOsSocket); + } + None +} + /// Send to a raw UDP socket with optional packet TTL. /// If the packet_ttl option is <=0, packet is sent with the default TTL. TTL setting is only used /// in ZeroTier right now to do escalating TTL probes for IPv4 NAT traversal. @@ -303,58 +316,42 @@ impl Drop for FastUDPSocket { } } -/* #[cfg(test)] mod tests { use crate::fastudpsocket::*; use zerotier_core::{InetAddress, Buffer}; - use std::sync::Arc; use std::sync::atomic::{AtomicU32, Ordering}; - #[allow(dead_code)] - struct TestPacketHandler { - cnt: AtomicU32, - side: &'static str - } - - impl FastUDPSocketPacketHandler for TestPacketHandler { - #[allow(unused)] - fn incoming_udp_packet(&self, raw_socket: &FastUDPRawOsSocket, from_adddress: &InetAddress, data: Buffer) { - self.cnt.fetch_add(1, Ordering::Relaxed); - //println!("{}: {} bytes from {} (socket: {})", self.side, data.len(), from_adddress.to_string().as_str(), *raw_socket); - } - } - #[test] fn test_udp_bind_and_transfer() { { - let ba1 = InetAddress::new_from_string("127.0.0.1/23333"); + let ba0 = InetAddress::new_from_string("127.0.0.1/23333"); + assert!(ba0.is_some()); + let ba0 = ba0.unwrap(); + let cnt0 = Arc::new(AtomicU32::new(0)); + let cnt0c = cnt0.clone(); + let s0 = FastUDPSocket::new("", &ba0, move |sock: &FastUDPRawOsSocket, _: &InetAddress, data: Buffer| { + cnt0c.fetch_add(1, Ordering::Relaxed); + }); + assert!(s0.is_ok()); + let s0 = s0.unwrap(); + + let ba1 = InetAddress::new_from_string("127.0.0.1/23334"); assert!(ba1.is_some()); let ba1 = ba1.unwrap(); - let h1: Arc = Arc::new(TestPacketHandler { - cnt: AtomicU32::new(0), - side: "Alice", + let cnt1 = Arc::new(AtomicU32::new(0)); + let cnt1c = cnt1.clone(); + let s1 = FastUDPSocket::new("", &ba1, move |sock: &FastUDPRawOsSocket, _: &InetAddress, data: Buffer| { + cnt1c.fetch_add(1, Ordering::Relaxed); }); - let s1 = FastUDPSocket::new("lo0", &ba1, &h1); assert!(s1.is_ok()); - let s1 = s1.ok().unwrap(); - - let ba2 = InetAddress::new_from_string("127.0.0.1/23334"); - assert!(ba2.is_some()); - let ba2 = ba2.unwrap(); - let h2: Arc = Arc::new(TestPacketHandler { - cnt: AtomicU32::new(0), - side: "Bob", - }); - let s2 = FastUDPSocket::new("lo0", &ba2, &h2); - assert!(s2.is_ok()); - let s2 = s2.ok().unwrap(); + let s1 = s1.unwrap(); let data_bytes = [0_u8; 1024]; loop { - s1.send(&ba2, data_bytes.as_ptr(), data_bytes.len(), 0); - s2.send(&ba1, data_bytes.as_ptr(), data_bytes.len(), 0); - if h1.cnt.load(Ordering::Relaxed) > 10000 && h2.cnt.load(Ordering::Relaxed) > 10000 { + s0.send(&ba1, data_bytes.as_ptr(), data_bytes.len(), 0); + s1.send(&ba0, data_bytes.as_ptr(), data_bytes.len(), 0); + if cnt0.load(Ordering::Relaxed) > 10000 && cnt1.load(Ordering::Relaxed) > 10000 { break; } } @@ -362,4 +359,3 @@ mod tests { //println!("FastUDPSocket shutdown successful"); } } -*/ diff --git a/rust-zerotier-service/src/getifaddrs.rs b/rust-zerotier-service/src/getifaddrs.rs index 62a580462..05b8f9ff5 100644 --- a/rust-zerotier-service/src/getifaddrs.rs +++ b/rust-zerotier-service/src/getifaddrs.rs @@ -11,11 +11,14 @@ */ /****/ -use zerotier_core::InetAddress; use std::ffi::CStr; -use std::ptr::{null_mut, copy_nonoverlapping}; use std::mem::size_of; +use std::ptr::{copy_nonoverlapping, null_mut}; + use num_traits::cast::AsPrimitive; + +use zerotier_core::InetAddress; + use crate::osdep as osdep; #[inline(always)] diff --git a/rust-zerotier-service/src/log.rs b/rust-zerotier-service/src/log.rs index 4b496c1d2..3f2fe2a98 100644 --- a/rust-zerotier-service/src/log.rs +++ b/rust-zerotier-service/src/log.rs @@ -15,19 +15,24 @@ use std::cell::Cell; use std::fmt::Display; use std::fs::{File, OpenOptions}; use std::io::{Seek, SeekFrom, Write}; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering, AtomicBool}; use std::sync::Mutex; use chrono::Datelike; use zerotier_core::PortableAtomicI64; +struct LogIntl { + file: Option, + cur_size: u64, + max_size: usize, + enabled: bool +} + pub(crate) struct Log { prefix: String, path: String, - file: Mutex>>, - cur_size: PortableAtomicI64, - max_size: AtomicUsize, + intl: Mutex } impl Log { @@ -41,53 +46,81 @@ impl Log { Log{ prefix: p, path: String::from(path), - file: Mutex::new(Cell::new(None)), - cur_size: PortableAtomicI64::new(0), - max_size: AtomicUsize::new(if max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { max_size }), + intl: Mutex::new(LogIntl { + file: None, + cur_size: 0, + max_size: if max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { max_size }, + enabled: true, + }), } } pub fn set_max_size(&self, new_max_size: usize) { - self.max_size.store(if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size },Ordering::Relaxed); + self.intl.lock().unwrap().max_size = if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size }; } - pub fn log>(&self, s: S) { - let mut fc = self.file.lock().unwrap(); + pub fn set_enabled(&self, enabled: bool) { + self.intl.lock().unwrap().enabled = enabled; + } + + pub fn log>(&self, s: S) { + let mut l = self.intl.lock().unwrap(); + if l.enabled { + if l.file.is_none() { + let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str()); + if f.is_err() { + return; + } + let mut f = f.unwrap(); + let eof = f.seek(SeekFrom::End(0)); + if eof.is_err() { + return; + } + l.cur_size = eof.unwrap(); + l.file = Some(f); + } + + if l.max_size > 0 && l.cur_size > l.max_size as u64 { + l.file = None; + l.cur_size = 0; - let max_size = self.max_size.load(Ordering::Relaxed); - if max_size > 0 && fc.get_mut().is_some() { - if self.cur_size.get() >= max_size as i64 { - fc.replace(None); // close and dispose of old File let mut old_path = self.path.clone(); old_path.push_str(".old"); let _ = std::fs::remove_file(old_path.as_str()); let _ = std::fs::rename(self.path.as_str(), old_path.as_str()); let _ = std::fs::remove_file(self.path.as_str()); // should fail - self.cur_size.set(0); - } - } - if fc.get_mut().is_none() { - let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str()); - if f.is_err() { - return; + let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str()); + if f.is_err() { + return; + } + l.file = Some(f.unwrap()); } - let mut f = f.unwrap(); - let eof = f.seek(SeekFrom::End(0)); - if eof.is_err() { - return; - } - self.cur_size.set(eof.unwrap() as i64); - fc.replace(Some(f)); - } - let mut f = fc.get_mut().as_mut().unwrap(); - let now_str = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); - let log_line = format!("{}[{}] {}\n", self.prefix.as_str(), now_str.as_str(), s.into()); - let _ = f.write_all(log_line.as_bytes()); - let _ = f.flush(); - self.cur_size.fetch_add(log_line.len() as i64); + let f = l.file.as_mut().unwrap(); + let now_str = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); + let ss: &str = s.as_ref(); + let log_line = format!("{}[{}] {}\n", self.prefix.as_str(), now_str.as_str(), ss); + let _ = f.write_all(log_line.as_bytes()); + let _ = f.flush(); + l.cur_size += log_line.len() as u64; + } } } unsafe impl Sync for Log {} + +/* +#[cfg(test)] +mod tests { + use crate::log::Log; + + #[test] + fn test_log() { + let l = Log::new("/tmp/ztlogtest.log", 65536, ""); + for i in 0..100000 { + l.log(format!("line {}", i)) + } + } +} +*/ diff --git a/rust-zerotier-service/src/main.rs b/rust-zerotier-service/src/main.rs index 391c645d5..03a220654 100644 --- a/rust-zerotier-service/src/main.rs +++ b/rust-zerotier-service/src/main.rs @@ -29,11 +29,14 @@ use std::boxed::Box; use std::ffi::CStr; use std::path::Path; +use crate::store::Store; +use std::sync::Arc; + fn main() { let mut process_exit_value: i32 = 0; - let mut zerotier_path = unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), 256) }; - let mut cli_args = Some(Box::new(cli::parse_cli_args())); + let mut cli_args = Some(Box::new(cli::parse_cli_args())); + let mut zerotier_path = unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), 256) }; let json_output; let mut token: Option = None; let mut token_path = Path::new(&zerotier_path).join("authtoken.secret"); @@ -54,6 +57,13 @@ fn main() { } } + let store = Store::new(zerotier_path.as_str()); + if store.is_err() { + println!("FATAL: error accessing directory '{}': {}", zerotier_path, store.err().unwrap().to_string()); + std::process::exit(1); + } + let store = Arc::new(store.unwrap()); + match cli_args.as_ref().unwrap().subcommand_name().unwrap() { "version" => { let ver = zerotier_core::version(); @@ -61,7 +71,7 @@ fn main() { }, "service" => { cli_args = None; // free any memory we can when launching service - process_exit_value = commands::service::run() + process_exit_value = commands::service::run(&store); }, _ => cli::print_help(), // includes "help" } diff --git a/rust-zerotier-service/src/store.rs b/rust-zerotier-service/src/store.rs index 591fb5ece..52968eda6 100644 --- a/rust-zerotier-service/src/store.rs +++ b/rust-zerotier-service/src/store.rs @@ -12,20 +12,36 @@ /****/ use std::error::Error; -use std::path::{Path, PathBuf}; -use zerotier_core::StateObjectType; use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::ffi::CString; + +use zerotier_core::{StateObjectType, NetworkId}; + +use crate::localconfig::LocalConfig; pub(crate) struct Store { pub base_path: Box, - pub peers_path: Box, - pub controller_path: Box, - pub networks_path: Box, - pub certs_path: Box, + pub default_log_path: Box, + peers_path: Box, + controller_path: Box, + networks_path: Box, + certs_path: Box, +} + +/// Restrict file permissions using OS-specific code in osdep/OSUtils.cpp. +pub fn lock_down_file(path: &str) { + let p = CString::new(path.as_bytes()); + if p.is_ok() { + let p = p.unwrap(); + unsafe { + crate::osdep::lockDownFile(p.as_ptr(), 0); + } + } } impl Store { - const MAX_OBJECT_SIZE: usize = 131072; // sanity limit + const MAX_OBJECT_SIZE: usize = 262144; // sanity limit pub fn new(base_path: &str) -> std::io::Result { let bp = Path::new(base_path); @@ -33,73 +49,180 @@ impl Store { if !md.is_dir() || md.permissions().readonly() { return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "base path does not exist or is not writable")); } - Ok(Store{ + + let s = Store { base_path: bp.to_path_buf().into_boxed_path(), + default_log_path: bp.join("service.log").into_boxed_path(), peers_path: bp.join("peers.d").into_boxed_path(), controller_path: bp.join("controller.d").into_boxed_path(), networks_path: bp.join("networks.d").into_boxed_path(), certs_path: bp.join("certs.d").into_boxed_path(), - }) + }; + + let _ = std::fs::create_dir_all(&s.peers_path); + let _ = std::fs::create_dir_all(&s.controller_path); + let _ = std::fs::create_dir_all(&s.networks_path); + let _ = std::fs::create_dir_all(&s.certs_path); + + Ok(s) } - fn make_obj_path(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option { - Some(match obj_type { - StateObjectType::IdentityPublic => self.base_path.join("identity.public"), - StateObjectType::IdentitySecret => self.base_path.join("identity.secret"), + fn make_obj_path(&self, obj_type: &StateObjectType, obj_id: &[u64]) -> Option { + match obj_type { + StateObjectType::IdentityPublic => { + Some(self.base_path.join("identity.public")) + }, + StateObjectType::IdentitySecret => { + Some(self.base_path.join("identity.secret")) + }, StateObjectType::Certificate => { if obj_id.len() < 6 { - return None; + None + } else { + Some(self.certs_path.join(format!("{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}.cert", obj_id[0], obj_id[1], obj_id[2], obj_id[3], obj_id[4], obj_id[5]))) } - self.certs_path.join(format!("{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}.cert",obj_id[0],obj_id[1],obj_id[2],obj_id[3],obj_id[4],obj_id[5])) }, - StateObjectType::TrustStore => self.base_path.join("truststore"), - StateObjectType::Locator => self.base_path.join("locator"), + StateObjectType::TrustStore => { + Some(self.base_path.join("truststore")) + }, + StateObjectType::Locator => { + Some(self.base_path.join("locator")) + }, StateObjectType::NetworkConfig => { if obj_id.len() < 1 { - return None; + None + } else { + Some(self.networks_path.join(format!("{:0>16x}.conf", obj_id[0]))) } - self.networks_path.join(format!("{:0>16x}.conf", obj_id[0])) }, StateObjectType::Peer => { if obj_id.len() < 1 { - return None; + None + } else { + Some(self.peers_path.join(format!("{:0>10x}.peer", obj_id[0]))) } - self.peers_path.join(format!("{:0>10x}.peer", obj_id[0])) } - }) + } } - pub fn load(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result> { - let obj_path = self.make_obj_path(obj_type, obj_id); - if obj_path.is_some() { - let obj_path = obj_path.unwrap(); - let fmd = obj_path.metadata()?; - if fmd.is_file() { - let flen = fmd.len(); - if flen <= Store::MAX_OBJECT_SIZE as u64 { - let mut f = std::fs::File::open(obj_path)?; - let mut buf: Vec = Vec::new(); - buf.reserve(flen as usize); - let rs = f.read_to_end(&mut buf)?; - buf.resize(rs as usize, 0); - return Ok(buf.into_boxed_slice()); - } + /// Class-internal read function + fn read_internal(&self, path: PathBuf) -> std::io::Result> { + let fmd = path.metadata()?; + if fmd.is_file() { + let flen = fmd.len(); + if flen <= Store::MAX_OBJECT_SIZE as u64 { + let mut f = std::fs::File::open(path)?; + let mut buf: Vec = Vec::new(); + buf.reserve(flen as usize); + let rs = f.read_to_end(&mut buf)?; + buf.resize(rs as usize, 0); + return Ok(buf); } } Err(std::io::Error::new(std::io::ErrorKind::NotFound, "does not exist or is not readable")) } - pub fn erase(&self, obj_type: StateObjectType, obj_id: &[u64]) { + /// Get a list of the network IDs to which this node is joined. + /// This is used to recall networks on startup by enumerating networks.d + /// and telling the core to (re)join them all. + pub fn networks(&self) -> Vec { + let mut list: Vec = Vec::new(); + let d = std::fs::read_dir(self.networks_path.as_ref()); + if d.is_ok() { + for de in d.unwrap() { + if de.is_ok() { + let nn = de.unwrap().file_name(); + let n = nn.to_str().unwrap_or(""); + if n.len() == 21 && n.ends_with(".conf") { // ################.conf + let nwid = u64::from_str_radix(&n[0..16], 16); + if nwid.is_ok() { + list.push(NetworkId(nwid.unwrap())); + } + } + } + } + } + list + } + + /// Read a file located in the base ZeroTier home directory. + pub fn read_file(&self, fname: &str) -> std::io::Result> { + self.read_internal(self.base_path.join(fname)) + } + + /// Like read_file but also converts into a string. + pub fn read_file_str(&self, fname: &str) -> std::io::Result { + let data = self.read_file(fname)?; + let data = String::from_utf8(data); + if data.is_err() { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, data.err().unwrap().to_string())); + } + Ok(data.unwrap()) + } + + /// Write a file to the base ZeroTier home directory. + pub fn write_file(&self, fname: &str, data: &[u8]) -> std::io::Result<()> { + std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(self.base_path.join(fname))?.write_all(data) + } + + /// Reads local.conf and deserializes into a LocalConfig object. + pub fn read_local_conf(&self) -> std::io::Result { + let data = self.read_file_str("local.conf")?; + let lc = LocalConfig::new_from_json(data.as_str()); + if lc.is_err() { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, lc.err().unwrap())); + } + Ok(lc.unwrap()) + } + + /// Writes a LocalConfig object in JSON format to local.conf. + pub fn write_local_conf(&self, lc: &LocalConfig) -> std::io::Result<()> { + let json = lc.to_json(); + self.write_file("local.conf", json.as_bytes()) + } + + /// Reads the authtoken.secret file in the home directory. + pub fn read_authtoken_secret(&self) -> std::io::Result { + let data = self.read_file_str("authtoken.secret")?; + Ok(data.trim().to_string()) + } + + /// Write authtoken.secret and lock down file permissions. + pub fn write_authtoken_secret(&self, sec: &str) -> std::io::Result<()> { + let p = self.base_path.join("authtoken.secret"); + let _ = std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(&p)?.write_all(sec.as_bytes())?; + lock_down_file(p.to_str().unwrap()); + Ok(()) + } + + /// Load a ZeroTier core object. + pub fn load_object(&self, obj_type: &StateObjectType, obj_id: &[u64]) -> std::io::Result> { + let obj_path = self.make_obj_path(&obj_type, obj_id); + if obj_path.is_some() { + return self.read_internal(obj_path.unwrap()); + } + Err(std::io::Error::new(std::io::ErrorKind::NotFound, "does not exist or is not readable")) + } + + /// Erase a ZeroTier core object. + pub fn erase_object(&self, obj_type: &StateObjectType, obj_id: &[u64]) { let obj_path = self.make_obj_path(obj_type, obj_id); if obj_path.is_some() { let _ = std::fs::remove_file(obj_path.unwrap()); } } - pub fn store(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> { + /// Store a ZeroTier core object. + /// Permissions will also be restricted for some object types. + pub fn store_object(&self, obj_type: &StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> { let obj_path = self.make_obj_path(obj_type, obj_id); if obj_path.is_some() { - std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(obj_path.unwrap())?.write_all(obj_data) + let obj_path = obj_path.unwrap(); + std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(&obj_path)?.write_all(obj_data)?; + if obj_type.eq(&StateObjectType::IdentitySecret) || obj_type.eq(&StateObjectType::TrustStore) { + lock_down_file(obj_path.to_str().unwrap()); + } + Ok(()) } else { Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "object ID not valid")) }