diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 4e6364013..30b8a872c 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,28 @@ ZeroTier Release Notes ====== +# 2020-11-19 -- Version 1.6.0 + +Version 1.6.0 is a major release that incorporates back-ported features from the 2.0 branch, which is still under development. It also fixes a number of issues. + +New features and improvements (including those listed under 1.5.0): + + * **Apple Silicon** (MacOS ARM64) native support via universal binary. ZeroTier now requires the very latest Xcode to build. + * **Linux performance improvements** for up to 25% faster tun/tap I/O performance on multi-core systems. + * **Multipath support** with modes modeled after the Linux kernel's bonding driver. This includes active-passive and active-active modes with fast failover and load balancing. See section 2.1.5 of the manual. + * **DNS configuration** push from network controllers to end nodes, with locally configurable permissions for whether or not push is allowed. + * **AES-GMAC-SIV** encryption mode, which is both somewhat more secure and significantly faster than the old Salsa20/12-Poly1305 mode on hardware that supports AES acceleration. This includes virtually all X86-64 chips and most ARM64. This mode is based on AES-SIV and has been audited by Trail of Bits to ensure that it is equivalent security-wise. + +Bug fixes: + + * **Managed route assignment fixes** to eliminate missing routes on Linux and what we believe to be the source of sporadic high CPU usage on MacOS. + * **Hang on shutdown** issues should be fixed. + * **Sporadic multicast outages** should be fixed. + +Known remaining issues: + + * AES hardware acceleration is not yet supported on 32-bit ARM, PowerPC (32 or 64), or MIPS (32 or 64) systems. Currently supported are X86-64 and ARM64/AARCH64 with crypto extensions. + # 2020-10-05 -- Version 1.5.0 (actually 1.6.0-beta1) Version 1.6.0 (1.5.0 is a beta!) is a significant release that incorporates a number of back-ported fixes and features from the ZeroTier 2.0 tree. diff --git a/debian/changelog b/debian/changelog index 417e7168a..f921505dd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +zerotier-one (1.6.0) unstable; urgency=medium + + * See RELEASE-NOTES.md for release notes. + + -- Adam Ierymenko Thu, 19 Nov 2020 01:00:00 -0700 + zerotier-one (1.5.0) unstable; urgency=medium * Version 1.5.0 is actually 1.6.0-beta1 diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj index ad9aa0604..42a377d83 100755 --- a/ext/installfiles/mac/ZeroTier One.pkgproj +++ b/ext/installfiles/mac/ZeroTier One.pkgproj @@ -689,7 +689,7 @@ USE_HFS+_COMPRESSION VERSION - 1.5.0 + 1.6.0 TYPE 0 diff --git a/make-mac.mk b/make-mac.mk index d476e9b0d..888be7e77 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -3,7 +3,7 @@ CXX=clang++ INCLUDES= DEFS= LIBS= -ARCH_FLAGS=-msse -msse2 -arch x86_64 -arch arm64e +ARCH_FLAGS=-msse -msse2 -arch x86_64 -arch arm64 CODESIGN=echo PRODUCTSIGN=echo @@ -67,6 +67,7 @@ endif # Debug mode -- dump trace output, build binary with -g ifeq ($(ZT_DEBUG),1) ZT_TRACE=1 + ARCH_FLAGS= CFLAGS+=-Wall -g $(INCLUDES) $(DEFS) $(ARCH_FLAGS) STRIP=echo # The following line enables optimization for the crypto code, since @@ -92,10 +93,10 @@ CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++ all: one macui ext/x64-salsa2012-asm/salsa2012.o: - as -o ext/x64-salsa2012-asm/salsa2012.o ext/x64-salsa2012-asm/salsa2012.s + as -arch x86_64 -o ext/x64-salsa2012-asm/salsa2012.o ext/x64-salsa2012-asm/salsa2012.s mac-agent: FORCE - $(CC) -Ofast -o MacEthernetTapAgent osdep/MacEthernetTapAgent.c + $(CC) -Ofast $(ARCH_FLAGS) -o MacEthernetTapAgent osdep/MacEthernetTapAgent.c $(CODESIGN) -f --options=runtime -s $(CODESIGN_APP_CERT) MacEthernetTapAgent osdep/MacDNSHelper.o: osdep/MacDNSHelper.mm diff --git a/osdep/EthernetTap.cpp b/osdep/EthernetTap.cpp index f30a96e60..4f3d20f40 100644 --- a/osdep/EthernetTap.cpp +++ b/osdep/EthernetTap.cpp @@ -96,6 +96,35 @@ std::shared_ptr EthernetTap::newInstance( #endif // __LINUX__ #ifdef __WINDOWS__ + HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) { + throw std::runtime_error("WinEthernetTap: COM initialization failed"); + } + + static bool _comInit = false; + static Mutex _comInit_m; + + { + Mutex::Lock l(_comInit_m); + if (!_comInit) { + hres = CoInitializeSecurity( + NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + NULL + ); + if (FAILED(hres)) { + CoUninitialize(); + throw std::runtime_error("WinEthernetTap: Failed to initialize security"); + } + _comInit = true; + } + } return std::shared_ptr(new WindowsEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg)); #endif // __WINDOWS__ diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 9306dc34c..8a557c077 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -184,111 +184,122 @@ LinuxEthernetTap::LinuxEthernetTap( (void)::pipe(_shutdownSignalPipe); - _tapReaderThread = std::thread([this]{ - fd_set readfds,nullfds; - int n,nfds,r; - void *buf = nullptr; + for(unsigned int t=0;t<2;++t) { + _tapReaderThread[t] = std::thread([this, t]{ + fd_set readfds,nullfds; + int n,nfds,r; + void *buf = nullptr; + std::vector buffers; - { - struct ifreq ifr; - memset(&ifr,0,sizeof(ifr)); - strcpy(ifr.ifr_name,_dev.c_str()); + if (t == 0) { + struct ifreq ifr; + memset(&ifr,0,sizeof(ifr)); + strcpy(ifr.ifr_name,_dev.c_str()); - const int sock = socket(AF_INET,SOCK_DGRAM,0); - if (sock <= 0) - return; + const int sock = socket(AF_INET,SOCK_DGRAM,0); + if (sock <= 0) + return; + + if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) { + ::close(sock); + printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n"); + return; + } + ifr.ifr_flags |= IFF_UP; + if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) { + ::close(sock); + printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n"); + return; + } + + // Some kernel versions seem to require you to yield while the device comes up + // before they will accept MTU and MAC. For others it doesn't matter, but is + // harmless. This was moved to the worker thread though so as not to block the + // main ZeroTier loop. + usleep(500000); + + ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER; + _mac.copyTo(ifr.ifr_ifru.ifru_hwaddr.sa_data,6); + if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) { + ::close(sock); + printf("WARNING: ioctl() failed setting up Linux tap device (set MAC)\n"); + return; + } + + ifr.ifr_ifru.ifru_mtu = (int)_mtu; + if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) { + ::close(sock); + printf("WARNING: ioctl() failed setting up Linux tap device (set MTU)\n"); + return; + } + + fcntl(_fd,F_SETFL,O_NONBLOCK); - if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) { ::close(sock); - printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n"); - return; - } - ifr.ifr_flags |= IFF_UP; - if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) { - ::close(sock); - printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n"); - return; + } else { + usleep(1500000); } - // Some kernel versions seem to require you to yield while the device comes up - // before they will accept MTU and MAC. For others it doesn't matter, but is - // harmless. This was moved to the worker thread though so as not to block the - // main ZeroTier loop. - usleep(500000); + FD_ZERO(&readfds); + FD_ZERO(&nullfds); + nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER; - _mac.copyTo(ifr.ifr_ifru.ifru_hwaddr.sa_data,6); - if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) { - ::close(sock); - printf("WARNING: ioctl() failed setting up Linux tap device (set MAC)\n"); - return; - } + r = 0; + for(;;) { + FD_SET(_shutdownSignalPipe[0],&readfds); + FD_SET(_fd,&readfds); + select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - ifr.ifr_ifru.ifru_mtu = (int)_mtu; - if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) { - ::close(sock); - printf("WARNING: ioctl() failed setting up Linux tap device (set MTU)\n"); - return; - } + if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread + break; - fcntl(_fd,F_SETFL,O_NONBLOCK); - - ::close(sock); - } - - 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)) { - for(;;) { // read until there are no more packets, then return to outer select() loop - if (!buf) { - std::lock_guard l(_buffers_l); - if (_buffers.empty()) { - buf = malloc(ZT_TAP_BUF_SIZE); - if (!buf) - break; - } else { - buf = _buffers.back(); - _buffers.pop_back(); - } - } - - n = (int)::read(_fd,reinterpret_cast(buf) + r,ZT_TAP_BUF_SIZE - r); - - if (n > 0) { - // 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) { - _tapq.post(std::pair(buf,r)); - buf = nullptr; + if (FD_ISSET(_fd,&readfds)) { + for(;;) { // read until there are no more packets, then return to outer select() loop + if (!buf) { + // To reduce use of the mutex, we keep a local buffer vector and + // swap (which is a pointer swap) with the global one when it's + // empty. This retrieves a batch of buffers to use. + if (buffers.empty()) { + std::lock_guard l(_buffers_l); + buffers.swap(_buffers); + } + if (buffers.empty()) { + buf = malloc(ZT_TAP_BUF_SIZE); + if (!buf) + break; + } else { + buf = buffers.back(); + buffers.pop_back(); } - - r = 0; } - } else { - r = 0; - break; + + n = (int)::read(_fd,reinterpret_cast(buf) + r,ZT_TAP_BUF_SIZE - r); + + if (n > 0) { + // 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) { + _tapq.post(std::pair(buf,r)); + buf = nullptr; + } + + r = 0; + } + } else { + r = 0; + break; + } } } } - } - }); + }); + } _tapProcessorThread = std::thread([this] { MAC to,from; @@ -302,7 +313,9 @@ LinuxEthernetTap::LinuxEthernetTap( _handler(_arg, nullptr, _nwid, from, to, etherType, 0, (const void *)(b + 14),(unsigned int)(qi.second - 14)); { std::lock_guard l(_buffers_l); - _buffers.push_back(qi.first); + if (_buffers.size() < 128) + _buffers.push_back(qi.first); + else free(qi.first); } } else break; } @@ -311,14 +324,15 @@ LinuxEthernetTap::LinuxEthernetTap( LinuxEthernetTap::~LinuxEthernetTap() { - (void)::write(_shutdownSignalPipe[1],"\0",1); // causes reader thread to exit + (void)::write(_shutdownSignalPipe[1],"\0",1); // causes reader thread(s) to exit _tapq.post(std::pair(nullptr,0)); // causes processor thread to exit ::close(_fd); ::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[1]); - _tapReaderThread.join(); + _tapReaderThread[0].join(); + _tapReaderThread[1].join(); _tapProcessorThread.join(); for(std::vector::iterator i(_buffers.begin());i!=_buffers.end();++i) diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp index 9e9206ead..a21a53a2c 100644 --- a/osdep/LinuxEthernetTap.hpp +++ b/osdep/LinuxEthernetTap.hpp @@ -70,7 +70,7 @@ private: int _fd; int _shutdownSignalPipe[2]; std::atomic_bool _enabled; - std::thread _tapReaderThread; + std::thread _tapReaderThread[2]; std::thread _tapProcessorThread; std::mutex _buffers_l; std::vector _buffers; diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index 596fec340..71c7c503d 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -474,29 +474,6 @@ WindowsEthernetTap::WindowsEthernetTap( char data[1024]; char tag[24]; - // Initialize COM - HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED); - if (FAILED(hres)) { - throw std::runtime_error("WinEthernetTap: COM initialization failed"); - } - - hres = CoInitializeSecurity( - NULL, - -1, - NULL, - NULL, - RPC_C_AUTHN_LEVEL_DEFAULT, - RPC_C_IMP_LEVEL_IMPERSONATE, - NULL, - EOAC_NONE, - NULL - ); - if (FAILED(hres)) { - CoUninitialize(); - throw std::runtime_error("WinEthernetTap: Failed to initialize security"); - } - - // We "tag" registry entries with the network ID to identify persistent devices OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid); @@ -671,7 +648,6 @@ WindowsEthernetTap::WindowsEthernetTap( WindowsEthernetTap::~WindowsEthernetTap() { WinDNSHelper::removeDNS(_nwid); - CoUninitialize(); _run = false; ReleaseSemaphore(_injectSemaphore,1,NULL); Thread::join(_thread); @@ -970,6 +946,12 @@ NET_IFINDEX WindowsEthernetTap::interfaceIndex() const void WindowsEthernetTap::threadMain() throw() { + HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) { + fprintf(stderr, "WinEthernetTap: COM initialization failed"); + return; + } + char tapReadBuf[ZT_MAX_MTU + 32]; char tapPath[128]; HANDLE wait4[3]; @@ -1188,6 +1170,7 @@ void WindowsEthernetTap::threadMain() // 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 + CoUninitialize(); } NET_IFINDEX WindowsEthernetTap::_getDeviceIndex() diff --git a/windows/WinUI/AboutView.xaml b/windows/WinUI/AboutView.xaml index d693c51da..f57c15c95 100644 --- a/windows/WinUI/AboutView.xaml +++ b/windows/WinUI/AboutView.xaml @@ -19,7 +19,7 @@ - + diff --git a/zerotier-one.spec b/zerotier-one.spec index a1cf24c25..452b3b5c1 100644 --- a/zerotier-one.spec +++ b/zerotier-one.spec @@ -1,5 +1,5 @@ Name: zerotier-one -Version: 1.5.0 +Version: 1.6.0 Release: 1%{?dist} Summary: ZeroTier One network virtualization service @@ -145,6 +145,9 @@ esac %endif %changelog +* Thu Nov 19 2020 Adam Ierymenko - 1.6.0-0.1 +- see https://github.com/zerotier/ZeroTierOne for release notes + * Mon Oct 05 2020 Adam Ierymenko - 1.6.0-beta1 - see https://github.com/zerotier/ZeroTierOne for release notes