Try another approach...

This commit is contained in:
Adam Ierymenko 2020-11-18 22:26:06 -05:00
parent ca428233ba
commit 414c248cc5
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3

View file

@ -184,118 +184,122 @@ LinuxEthernetTap::LinuxEthernetTap(
(void)::pipe(_shutdownSignalPipe); (void)::pipe(_shutdownSignalPipe);
_tapReaderThread = std::thread([this]{ for(unsigned int t=0;t<2;++t) {
fd_set readfds,nullfds; _tapReaderThread = std::thread([this, t]{
int n,nfds,r; fd_set readfds,nullfds;
void *buf = nullptr; int n,nfds,r;
std::vector<void *> buffers; void *buf = nullptr;
std::vector<void *> buffers;
{ if (t == 0) {
struct ifreq ifr; struct ifreq ifr;
memset(&ifr,0,sizeof(ifr)); memset(&ifr,0,sizeof(ifr));
strcpy(ifr.ifr_name,_dev.c_str()); strcpy(ifr.ifr_name,_dev.c_str());
const int sock = socket(AF_INET,SOCK_DGRAM,0); const int sock = socket(AF_INET,SOCK_DGRAM,0);
if (sock <= 0) if (sock <= 0)
return; 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); ::close(sock);
printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n"); } else {
return; usleep(1500000);
}
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 FD_ZERO(&readfds);
// before they will accept MTU and MAC. For others it doesn't matter, but is FD_ZERO(&nullfds);
// harmless. This was moved to the worker thread though so as not to block the nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
// main ZeroTier loop.
usleep(500000);
ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER; r = 0;
_mac.copyTo(ifr.ifr_ifru.ifru_hwaddr.sa_data,6); for(;;) {
if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) { FD_SET(_shutdownSignalPipe[0],&readfds);
::close(sock); FD_SET(_fd,&readfds);
printf("WARNING: ioctl() failed setting up Linux tap device (set MAC)\n"); select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
return;
}
ifr.ifr_ifru.ifru_mtu = (int)_mtu; if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) { break;
::close(sock);
printf("WARNING: ioctl() failed setting up Linux tap device (set MTU)\n");
return;
}
fcntl(_fd,F_SETFL,O_NONBLOCK); if (FD_ISSET(_fd,&readfds)) {
for(;;) { // read until there are no more packets, then return to outer select() loop
::close(sock); 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
FD_ZERO(&readfds); // empty. This retrieves a batch of buffers to use.
FD_ZERO(&nullfds); if (buffers.empty()) {
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; std::lock_guard<std::mutex> l(_buffers_l);
buffers.swap(_buffers);
r = 0; }
for(;;) { if (buffers.empty()) {
FD_SET(_shutdownSignalPipe[0],&readfds); buf = malloc(ZT_TAP_BUF_SIZE);
FD_SET(_fd,&readfds); if (!buf)
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); break;
} else {
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread buf = buffers.back();
break; buffers.pop_back();
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<std::mutex> 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<uint8_t *>(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<void *,int>(buf,r));
buf = nullptr;
} }
r = 0;
} }
} else {
r = 0; n = (int)::read(_fd,reinterpret_cast<uint8_t *>(buf) + r,ZT_TAP_BUF_SIZE - r);
break;
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<void *,int>(buf,r));
buf = nullptr;
}
r = 0;
}
} else {
r = 0;
break;
}
} }
} }
} }
} });
}); }
_tapProcessorThread = std::thread([this] { _tapProcessorThread = std::thread([this] {
MAC to,from; MAC to,from;