|
|
|
|
@ -60,7 +60,7 @@
|
|
|
|
|
#define IFNAMSIZ 16 |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#define ZT_TAP_BUF_SIZE 16384 |
|
|
|
|
#define ZT_TAP_BUF_SIZE (1024 * 16) |
|
|
|
|
|
|
|
|
|
// ff:ff:ff:ff:ff:ff with no ADI
|
|
|
|
|
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); |
|
|
|
|
@ -68,7 +68,7 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
|
|
|
|
|
namespace ZeroTier { |
|
|
|
|
|
|
|
|
|
// determine if we're running a really old linux kernel.
|
|
|
|
|
// Kernels in the 2.6.x series don't behave the same when bringing up
|
|
|
|
|
// Kernels in the 2.6.x series don't behave the same when bringing up
|
|
|
|
|
// the tap devices.
|
|
|
|
|
//
|
|
|
|
|
// Returns true if the kernel major version is < 3
|
|
|
|
|
@ -111,6 +111,7 @@ static void _base32_5_to_8(const uint8_t *in,char *out)
|
|
|
|
|
|
|
|
|
|
LinuxEthernetTap::LinuxEthernetTap( |
|
|
|
|
const char *homePath, |
|
|
|
|
unsigned int concurrency, |
|
|
|
|
const MAC &mac, |
|
|
|
|
unsigned int mtu, |
|
|
|
|
unsigned int metric, |
|
|
|
|
@ -127,6 +128,7 @@ LinuxEthernetTap::LinuxEthernetTap(
|
|
|
|
|
_fd(0), |
|
|
|
|
_enabled(true), |
|
|
|
|
_run(true), |
|
|
|
|
_concurrency(concurrency), |
|
|
|
|
_lastIfAddrsUpdate(0) |
|
|
|
|
{ |
|
|
|
|
static std::mutex s_tapCreateLock; |
|
|
|
|
@ -220,135 +222,164 @@ LinuxEthernetTap::LinuxEthernetTap(
|
|
|
|
|
|
|
|
|
|
(void)::pipe(_shutdownSignalPipe); |
|
|
|
|
|
|
|
|
|
_tapReaderThread = std::thread([this]{ |
|
|
|
|
uint8_t b[ZT_TAP_BUF_SIZE]; |
|
|
|
|
fd_set readfds,nullfds; |
|
|
|
|
int n,nfds,r; |
|
|
|
|
std::vector<void *> buffers; |
|
|
|
|
struct ifreq ifr; |
|
|
|
|
bool _enablePinning = false; |
|
|
|
|
char* envvar = std::getenv("ZT_CPU_PINNING"); |
|
|
|
|
if (envvar) { |
|
|
|
|
int tmp = atoi(envvar); |
|
|
|
|
if (tmp > 0) { |
|
|
|
|
_enablePinning = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
memset(&ifr,0,sizeof(ifr)); |
|
|
|
|
strcpy(ifr.ifr_name,_dev.c_str()); |
|
|
|
|
for (unsigned int i = 0; i < _concurrency; ++i) { |
|
|
|
|
_rxThreads.push_back(std::thread([this, i, _enablePinning] { |
|
|
|
|
|
|
|
|
|
if (_enablePinning) { |
|
|
|
|
int pinCore = i % _concurrency; |
|
|
|
|
fprintf(stderr, "pinning thread %d to core %d\n", i, pinCore); |
|
|
|
|
pthread_t self = pthread_self(); |
|
|
|
|
cpu_set_t cpuset; |
|
|
|
|
CPU_ZERO(&cpuset); |
|
|
|
|
CPU_SET(pinCore, &cpuset); |
|
|
|
|
int rc = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset); |
|
|
|
|
if (rc != 0) |
|
|
|
|
{ |
|
|
|
|
fprintf(stderr, "failed to pin thread %d to core %d: %s\n", i, pinCore, strerror(errno)); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const int sock = socket(AF_INET,SOCK_DGRAM,0); |
|
|
|
|
if (sock <= 0) |
|
|
|
|
return; |
|
|
|
|
uint8_t b[ZT_TAP_BUF_SIZE]; |
|
|
|
|
fd_set readfds, nullfds; |
|
|
|
|
int n, nfds, r; |
|
|
|
|
if (i == 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; |
|
|
|
|
|
|
|
|
|
if (ioctl(sock, SIOCGIFFLAGS, (void*)&ifr) < 0) { |
|
|
|
|
::close(sock); |
|
|
|
|
printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n"); |
|
|
|
|
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_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_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; |
|
|
|
|
} |
|
|
|
|
usleep(100000); |
|
|
|
|
|
|
|
|
|
usleep(100000); |
|
|
|
|
if (isOldLinuxKernel()) { |
|
|
|
|
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 (isOldLinuxKernel()) { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
usleep(100000); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
usleep(100000); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifr.ifr_flags |= IFF_MULTICAST; |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
ifr.ifr_flags |= IFF_MULTICAST; |
|
|
|
|
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(100000); |
|
|
|
|
usleep(100000); |
|
|
|
|
|
|
|
|
|
if (! isOldLinuxKernel()) { |
|
|
|
|
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 (!isOldLinuxKernel()) { |
|
|
|
|
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"); |
|
|
|
|
if (! _run) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fcntl(_fd,F_SETFL,O_NONBLOCK); |
|
|
|
|
|
|
|
|
|
::close(sock); |
|
|
|
|
|
|
|
|
|
if (!_run) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
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)) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
if (FD_ISSET(_fd,&readfds)) { |
|
|
|
|
for(;;) { // read until there are no more packets, then return to outer select() loop
|
|
|
|
|
n = (int)::read(_fd,b + 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;
|
|
|
|
|
MAC to(b, 6),from(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)(r - 14)); |
|
|
|
|
} |
|
|
|
|
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)) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if (FD_ISSET(_fd, &readfds)) { |
|
|
|
|
for (;;) { |
|
|
|
|
// read until there are no more packets, then return to outer select() loop
|
|
|
|
|
n = (int)::read(_fd, b + 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) { |
|
|
|
|
MAC to(b, 6), from(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)(r - 14)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
r = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
r = 0; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
r = 0; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LinuxEthernetTap::~LinuxEthernetTap() |
|
|
|
|
{ |
|
|
|
|
_run = false; |
|
|
|
|
(void)::write(_shutdownSignalPipe[1],"\0",1); |
|
|
|
|
_tapReaderThread.join(); |
|
|
|
|
::close(_fd); |
|
|
|
|
::close(_shutdownSignalPipe[0]); |
|
|
|
|
::close(_shutdownSignalPipe[1]); |
|
|
|
|
for (std::thread &t : _rxThreads) { |
|
|
|
|
t.join(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LinuxEthernetTap::setEnabled(bool en) |
|
|
|
|
|