From 7cbbc38b8967bddaebe4ef62f6f04ac1cd800ee3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 12:50:34 -0500 Subject: [PATCH 01/14] Reduce mutex use in LinuxEthernetTap. Try to squeeze a bit more powah out of it. --- osdep/LinuxEthernetTap.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 9306dc34c..31333dee3 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -188,6 +188,7 @@ LinuxEthernetTap::LinuxEthernetTap( fd_set readfds,nullfds; int n,nfds,r; void *buf = nullptr; + std::vector buffers; { struct ifreq ifr; @@ -252,14 +253,20 @@ LinuxEthernetTap::LinuxEthernetTap( 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()) { + // 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(); + buf = buffers.back(); + buffers.pop_back(); } } @@ -302,7 +309,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; } From e3e188109d601bb90b9c98443694367a5368682e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 10:14:39 -0800 Subject: [PATCH 02/14] Build fix. --- osdep/LinuxEthernetTap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 31333dee3..25ed52edf 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -188,7 +188,7 @@ LinuxEthernetTap::LinuxEthernetTap( fd_set readfds,nullfds; int n,nfds,r; void *buf = nullptr; - std::vector buffers; + std::vector buffers; { struct ifreq ifr; From a390629371970be9dffa9db7cd8af2a7004ee50c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:06:17 -0500 Subject: [PATCH 03/14] Try another optimization in LinuxEthernetTap. --- osdep/LinuxEthernetTap.cpp | 112 ++++++++++++++----------------------- osdep/LinuxEthernetTap.hpp | 1 - 2 files changed, 43 insertions(+), 70 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 25ed52edf..4659df556 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -182,14 +182,7 @@ LinuxEthernetTap::LinuxEthernetTap( _dev = ifr.ifr_name; ::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); - (void)::pipe(_shutdownSignalPipe); - _tapReaderThread = std::thread([this]{ - fd_set readfds,nullfds; - int n,nfds,r; - void *buf = nullptr; - std::vector buffers; - { struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); @@ -232,67 +225,51 @@ LinuxEthernetTap::LinuxEthernetTap( return; } - 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) { - // 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(); - } - } - - 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; - } + std::vector buffers; + void *buf = nullptr; + for(int r=0;;) { + 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(); + } + } + + const int 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; } } }); @@ -320,12 +297,9 @@ LinuxEthernetTap::LinuxEthernetTap( LinuxEthernetTap::~LinuxEthernetTap() { - (void)::write(_shutdownSignalPipe[1],"\0",1); // causes reader thread to exit - _tapq.post(std::pair(nullptr,0)); // causes processor thread to exit - + _tapq.post(std::pair(nullptr,0)); + ::shutdown(_fd, SHUT_RDWR); ::close(_fd); - ::close(_shutdownSignalPipe[0]); - ::close(_shutdownSignalPipe[1]); _tapReaderThread.join(); _tapProcessorThread.join(); diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp index 9e9206ead..f296ec693 100644 --- a/osdep/LinuxEthernetTap.hpp +++ b/osdep/LinuxEthernetTap.hpp @@ -68,7 +68,6 @@ private: std::vector _multicastGroups; unsigned int _mtu; int _fd; - int _shutdownSignalPipe[2]; std::atomic_bool _enabled; std::thread _tapReaderThread; std::thread _tapProcessorThread; From 11545bdb5d37b2df26fc5fdd00cadd05878de094 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:13:06 -0500 Subject: [PATCH 04/14] Shutdown fix. --- osdep/LinuxEthernetTap.cpp | 8 ++++++-- osdep/LinuxEthernetTap.hpp | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 4659df556..ad96eb9ed 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -92,7 +92,8 @@ LinuxEthernetTap::LinuxEthernetTap( _homePath(homePath), _mtu(mtu), _fd(0), - _enabled(true) + _enabled(true), + _running(true) { static std::mutex s_tapCreateLock; char procpath[128],nwids[32]; @@ -267,7 +268,7 @@ LinuxEthernetTap::LinuxEthernetTap( r = 0; } - } else { + } else if ((errno != EINTR)||(!_running.load())) { r = 0; break; } @@ -297,9 +298,12 @@ LinuxEthernetTap::LinuxEthernetTap( LinuxEthernetTap::~LinuxEthernetTap() { + _running = false; + _tapq.post(std::pair(nullptr,0)); ::shutdown(_fd, SHUT_RDWR); ::close(_fd); + pthread_kill(_tapReaderThread.native_handle(), SIGUSR1); _tapReaderThread.join(); _tapProcessorThread.join(); diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp index f296ec693..771b27980 100644 --- a/osdep/LinuxEthernetTap.hpp +++ b/osdep/LinuxEthernetTap.hpp @@ -69,6 +69,7 @@ private: unsigned int _mtu; int _fd; std::atomic_bool _enabled; + std::atomic_bool _running; std::thread _tapReaderThread; std::thread _tapProcessorThread; std::mutex _buffers_l; From 3712f9b318dc3a47b97bcc717266c4c0b100d4f8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:24:09 -0500 Subject: [PATCH 05/14] Revert "Shutdown fix." This reverts commit 11545bdb5d37b2df26fc5fdd00cadd05878de094. --- osdep/LinuxEthernetTap.cpp | 8 ++------ osdep/LinuxEthernetTap.hpp | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index ad96eb9ed..4659df556 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -92,8 +92,7 @@ LinuxEthernetTap::LinuxEthernetTap( _homePath(homePath), _mtu(mtu), _fd(0), - _enabled(true), - _running(true) + _enabled(true) { static std::mutex s_tapCreateLock; char procpath[128],nwids[32]; @@ -268,7 +267,7 @@ LinuxEthernetTap::LinuxEthernetTap( r = 0; } - } else if ((errno != EINTR)||(!_running.load())) { + } else { r = 0; break; } @@ -298,12 +297,9 @@ LinuxEthernetTap::LinuxEthernetTap( LinuxEthernetTap::~LinuxEthernetTap() { - _running = false; - _tapq.post(std::pair(nullptr,0)); ::shutdown(_fd, SHUT_RDWR); ::close(_fd); - pthread_kill(_tapReaderThread.native_handle(), SIGUSR1); _tapReaderThread.join(); _tapProcessorThread.join(); diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp index 771b27980..f296ec693 100644 --- a/osdep/LinuxEthernetTap.hpp +++ b/osdep/LinuxEthernetTap.hpp @@ -69,7 +69,6 @@ private: unsigned int _mtu; int _fd; std::atomic_bool _enabled; - std::atomic_bool _running; std::thread _tapReaderThread; std::thread _tapProcessorThread; std::mutex _buffers_l; From ca428233ba1957e93edf778cb7a38aaa484c15ce Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:24:18 -0500 Subject: [PATCH 06/14] Revert "Try another optimization in LinuxEthernetTap." This reverts commit a390629371970be9dffa9db7cd8af2a7004ee50c. --- osdep/LinuxEthernetTap.cpp | 102 +++++++++++++++++++++++-------------- osdep/LinuxEthernetTap.hpp | 1 + 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 4659df556..25ed52edf 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -182,7 +182,14 @@ LinuxEthernetTap::LinuxEthernetTap( _dev = ifr.ifr_name; ::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); + (void)::pipe(_shutdownSignalPipe); + _tapReaderThread = std::thread([this]{ + fd_set readfds,nullfds; + int n,nfds,r; + void *buf = nullptr; + std::vector buffers; + { struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); @@ -225,51 +232,67 @@ LinuxEthernetTap::LinuxEthernetTap( return; } + fcntl(_fd,F_SETFL,O_NONBLOCK); + ::close(sock); } - std::vector buffers; - void *buf = nullptr; - for(int r=0;;) { - 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(); - } - } + FD_ZERO(&readfds); + FD_ZERO(&nullfds); + nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - const int n = (int)::read(_fd,reinterpret_cast(buf) + r,ZT_TAP_BUF_SIZE - r); + r = 0; + for(;;) { + FD_SET(_shutdownSignalPipe[0],&readfds); + FD_SET(_fd,&readfds); + select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - 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 (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread + break; - 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; + 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; + } } - } else { - r = 0; - break; } } }); @@ -297,9 +320,12 @@ LinuxEthernetTap::LinuxEthernetTap( LinuxEthernetTap::~LinuxEthernetTap() { - _tapq.post(std::pair(nullptr,0)); - ::shutdown(_fd, SHUT_RDWR); + (void)::write(_shutdownSignalPipe[1],"\0",1); // causes reader thread to exit + _tapq.post(std::pair(nullptr,0)); // causes processor thread to exit + ::close(_fd); + ::close(_shutdownSignalPipe[0]); + ::close(_shutdownSignalPipe[1]); _tapReaderThread.join(); _tapProcessorThread.join(); diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp index f296ec693..9e9206ead 100644 --- a/osdep/LinuxEthernetTap.hpp +++ b/osdep/LinuxEthernetTap.hpp @@ -68,6 +68,7 @@ private: std::vector _multicastGroups; unsigned int _mtu; int _fd; + int _shutdownSignalPipe[2]; std::atomic_bool _enabled; std::thread _tapReaderThread; std::thread _tapProcessorThread; From 414c248cc5052bc219fcc695ed559b929045f8b0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:26:06 -0500 Subject: [PATCH 07/14] Try another approach... --- osdep/LinuxEthernetTap.cpp | 200 +++++++++++++++++++------------------ 1 file changed, 102 insertions(+), 98 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 25ed52edf..04871193f 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -184,118 +184,122 @@ LinuxEthernetTap::LinuxEthernetTap( (void)::pipe(_shutdownSignalPipe); - _tapReaderThread = std::thread([this]{ - fd_set readfds,nullfds; - int n,nfds,r; - void *buf = nullptr; - std::vector buffers; + for(unsigned int t=0;t<2;++t) { + _tapReaderThread = 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) { - // 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(); - } - } - - 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; From f934a09baaa360de742d96bf359f7de8472b4702 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:32:57 -0500 Subject: [PATCH 08/14] Build fix. --- osdep/LinuxEthernetTap.cpp | 7 ++++--- osdep/LinuxEthernetTap.hpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 04871193f..8a557c077 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -185,7 +185,7 @@ LinuxEthernetTap::LinuxEthernetTap( (void)::pipe(_shutdownSignalPipe); for(unsigned int t=0;t<2;++t) { - _tapReaderThread = std::thread([this, t]{ + _tapReaderThread[t] = std::thread([this, t]{ fd_set readfds,nullfds; int n,nfds,r; void *buf = nullptr; @@ -324,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; From 7b14aeb53e20727d304f97de33739c69770f2119 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:42:00 -0500 Subject: [PATCH 09/14] Tune thread count based on hardware concurrency. --- osdep/LinuxEthernetTap.cpp | 12 ++++++------ osdep/LinuxEthernetTap.hpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index 8a557c077..aeffede06 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -184,14 +184,14 @@ LinuxEthernetTap::LinuxEthernetTap( (void)::pipe(_shutdownSignalPipe); - for(unsigned int t=0;t<2;++t) { - _tapReaderThread[t] = std::thread([this, t]{ + for(unsigned int tn=0,tc=(unsigned int)std::thread::hardware_concurrency();tn<=tc;++tn) { + _tapReaderThreads.push_back(std::thread([this, tn]{ fd_set readfds,nullfds; int n,nfds,r; void *buf = nullptr; std::vector buffers; - if (t == 0) { + if (tn == 0) { struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); strcpy(ifr.ifr_name,_dev.c_str()); @@ -298,7 +298,7 @@ LinuxEthernetTap::LinuxEthernetTap( } } } - }); + })); } _tapProcessorThread = std::thread([this] { @@ -331,8 +331,8 @@ LinuxEthernetTap::~LinuxEthernetTap() ::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[1]); - _tapReaderThread[0].join(); - _tapReaderThread[1].join(); + for(std::vector::iterator t(_tapReaderThreads.begin());t!=_tapReaderThreads.end();++t) + t->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 a21a53a2c..8a3890bcb 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[2]; + std::vector _tapReaderThreads; std::thread _tapProcessorThread; std::mutex _buffers_l; std::vector _buffers; From a0c78da1ad3e388da219a7d3f065d43d4118b898 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Nov 2020 22:54:05 -0500 Subject: [PATCH 10/14] Revert "Tune thread count based on hardware concurrency." This reverts commit 7b14aeb53e20727d304f97de33739c69770f2119. --- osdep/LinuxEthernetTap.cpp | 12 ++++++------ osdep/LinuxEthernetTap.hpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index aeffede06..8a557c077 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -184,14 +184,14 @@ LinuxEthernetTap::LinuxEthernetTap( (void)::pipe(_shutdownSignalPipe); - for(unsigned int tn=0,tc=(unsigned int)std::thread::hardware_concurrency();tn<=tc;++tn) { - _tapReaderThreads.push_back(std::thread([this, tn]{ + 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; - if (tn == 0) { + if (t == 0) { struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); strcpy(ifr.ifr_name,_dev.c_str()); @@ -298,7 +298,7 @@ LinuxEthernetTap::LinuxEthernetTap( } } } - })); + }); } _tapProcessorThread = std::thread([this] { @@ -331,8 +331,8 @@ LinuxEthernetTap::~LinuxEthernetTap() ::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[1]); - for(std::vector::iterator t(_tapReaderThreads.begin());t!=_tapReaderThreads.end();++t) - t->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 8a3890bcb..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::vector _tapReaderThreads; + std::thread _tapReaderThread[2]; std::thread _tapProcessorThread; std::mutex _buffers_l; std::vector _buffers; From 565bef05afd7bf8fd8bf80d7bc0dace9bd2b67f0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 19 Nov 2020 13:24:30 -0500 Subject: [PATCH 11/14] Release notes and version bumps. --- RELEASE-NOTES.md | 22 ++++++++++++++++++++++ debian/changelog | 6 ++++++ ext/installfiles/mac/ZeroTier One.pkgproj | 2 +- windows/WinUI/AboutView.xaml | 2 +- zerotier-one.spec | 5 ++++- 5 files changed, 34 insertions(+), 3 deletions(-) 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/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 From 3ec932e19425fdf6f382227e6883748c142fb99b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 19 Nov 2020 13:43:57 -0500 Subject: [PATCH 12/14] MacOS ARM64 universal binary build fixes. --- make-mac.mk | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 From a9e4fb0ed9be2917b27ea0332ed7fd54cda068df Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 19 Nov 2020 14:10:34 -0800 Subject: [PATCH 13/14] Fix multiple networks on Windows --- osdep/EthernetTap.cpp | 29 +++++++++++++++++++++++++++++ osdep/WindowsEthernetTap.cpp | 29 ++++++----------------------- 2 files changed, 35 insertions(+), 23 deletions(-) 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/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index 596fec340..3048626a5 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); @@ -970,6 +947,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]; From 95da2bcbcfcd567a0a7e1041f1ea9c9346fcecfd Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 19 Nov 2020 14:12:24 -0800 Subject: [PATCH 14/14] move CoUninitialize to the proper place at the end of the tap thread --- osdep/WindowsEthernetTap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index 3048626a5..71c7c503d 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -648,7 +648,6 @@ WindowsEthernetTap::WindowsEthernetTap( WindowsEthernetTap::~WindowsEthernetTap() { WinDNSHelper::removeDNS(_nwid); - CoUninitialize(); _run = false; ReleaseSemaphore(_injectSemaphore,1,NULL); Thread::join(_thread); @@ -1171,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()