|
|
|
|
@ -11,6 +11,10 @@
|
|
|
|
|
*/ |
|
|
|
|
/****/ |
|
|
|
|
|
|
|
|
|
#ifdef __GNUC__ |
|
|
|
|
#pragma GCC diagnostic ignored "-Wrestrict" |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#include "../node/Constants.hpp" |
|
|
|
|
|
|
|
|
|
#ifdef __LINUX__ |
|
|
|
|
@ -52,6 +56,8 @@
|
|
|
|
|
#define IFNAMSIZ 16 |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#define ZT_TAP_BUF_SIZE 16384 |
|
|
|
|
|
|
|
|
|
// ff:ff:ff:ff:ff:ff with no ADI
|
|
|
|
|
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); |
|
|
|
|
|
|
|
|
|
@ -177,16 +183,153 @@ LinuxEthernetTap::LinuxEthernetTap(
|
|
|
|
|
|
|
|
|
|
(void)::pipe(_shutdownSignalPipe); |
|
|
|
|
|
|
|
|
|
_thread = Thread::start(this); |
|
|
|
|
_tapReaderThread = std::thread([this]{ |
|
|
|
|
fd_set readfds,nullfds; |
|
|
|
|
int n,nfds,r; |
|
|
|
|
void *buf = nullptr; |
|
|
|
|
|
|
|
|
|
usleep(100000); |
|
|
|
|
{ |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
::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(int x=0;x<64;++x) { |
|
|
|
|
if (!buf) { |
|
|
|
|
std::lock_guard<std::mutex> 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<uint8_t *>(buf) + r,ZT_TAP_BUF_SIZE - r); |
|
|
|
|
if (n < 0) { |
|
|
|
|
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) { |
|
|
|
|
_tapq.post(std::pair<void *,int>(buf,r)); |
|
|
|
|
buf = nullptr; |
|
|
|
|
/*
|
|
|
|
|
to.setTo(getBuf,6); |
|
|
|
|
from.setTo(getBuf + 6,6); |
|
|
|
|
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); |
|
|
|
|
// TODO: VLAN support
|
|
|
|
|
_handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14); |
|
|
|
|
*/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
r = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
_tapProcessorThread = std::thread([this] { |
|
|
|
|
MAC to,from; |
|
|
|
|
std::pair<void *,int> qi; |
|
|
|
|
while (_tapq.get(qi)) { |
|
|
|
|
uint8_t *const b = reinterpret_cast<uint8_t *>(qi.first); |
|
|
|
|
if (b) { |
|
|
|
|
to.setTo(b, 6); |
|
|
|
|
from.setTo(b + 6, 6); |
|
|
|
|
unsigned int etherType = Utils::ntoh(((const uint16_t *)b)[6]); |
|
|
|
|
_handler(_arg, nullptr, _nwid, from, to, etherType, 0, (const void *)(b + 14),(unsigned int)(qi.second - 14)); |
|
|
|
|
{ |
|
|
|
|
std::lock_guard<std::mutex> l(_buffers_l); |
|
|
|
|
_buffers.push_back(qi.first); |
|
|
|
|
} |
|
|
|
|
} else break; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LinuxEthernetTap::~LinuxEthernetTap() |
|
|
|
|
{ |
|
|
|
|
(void)::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
|
|
|
|
Thread::join(_thread); |
|
|
|
|
_tapq.post(std::pair<void *,int>(nullptr,0)); |
|
|
|
|
|
|
|
|
|
::close(_fd); |
|
|
|
|
::close(_shutdownSignalPipe[0]); |
|
|
|
|
::close(_shutdownSignalPipe[1]); |
|
|
|
|
|
|
|
|
|
_tapReaderThread.join(); |
|
|
|
|
_tapProcessorThread.join(); |
|
|
|
|
|
|
|
|
|
for(std::vector<void *>::iterator i(_buffers.begin());i!=_buffers.end();++i) |
|
|
|
|
free(*i); |
|
|
|
|
std::vector< std::pair<void *,int> > dv(_tapq.drain()); |
|
|
|
|
for(std::vector< std::pair<void *,int> >::iterator i(dv.begin());i!=dv.end();++i) { |
|
|
|
|
if (i->first) |
|
|
|
|
free(i->first); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LinuxEthernetTap::setEnabled(bool en) |
|
|
|
|
@ -400,107 +543,6 @@ void LinuxEthernetTap::setMtu(unsigned int mtu)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LinuxEthernetTap::threadMain() |
|
|
|
|
throw() |
|
|
|
|
{ |
|
|
|
|
fd_set readfds,nullfds; |
|
|
|
|
MAC to,from; |
|
|
|
|
int n,nfds,r; |
|
|
|
|
char getBuf[ZT_MAX_MTU + 64]; |
|
|
|
|
|
|
|
|
|
Thread::sleep(100); |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Thread::sleep(500); |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fcntl(_fd,F_SETFL,O_NONBLOCK) == -1) { |
|
|
|
|
::close(sock); |
|
|
|
|
printf("WARNING: ioctl() failed setting up Linux tap device (set non-blocking)\n"); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
::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(int x=0;x<64;++x) { |
|
|
|
|
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); |
|
|
|
|
if (n < 0) { |
|
|
|
|
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,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
r = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace ZeroTier
|
|
|
|
|
|
|
|
|
|
#endif // __LINUX__
|
|
|
|
|
|