summaryrefslogtreecommitdiff
path: root/node/EthernetTap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/EthernetTap.cpp')
-rw-r--r--node/EthernetTap.cpp562
1 files changed, 230 insertions, 332 deletions
diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp
index 62eee69c..9158e937 100644
--- a/node/EthernetTap.cpp
+++ b/node/EthernetTap.cpp
@@ -30,15 +30,17 @@
#include "EthernetTap.hpp"
#include "Logger.hpp"
#include "RuntimeEnvironment.hpp"
+#include "Utils.hpp"
#include "Mutex.hpp"
-#include "MulticastGroup.hpp"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
-/* ======================================================================== */
-#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
-/* ======================================================================== */
+//
+// TAP implementation for *nix OSes, with some specialization for different flavors
+//
+
+#ifdef __UNIX_LIKE__ /////////////////////////////////////////////////////////
#include <stdint.h>
#include <stdio.h>
@@ -52,10 +54,13 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
+#include <sys/select.h>
#include <netinet/in.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
+#ifdef __LINUX__
+
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_addr.h>
@@ -64,23 +69,49 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
#define ZT_ETHERTAP_IP_COMMAND "/sbin/ip"
#define ZT_ETHERTAP_SYSCTL_COMMAND "/sbin/sysctl"
+#endif // __LINUX__
+
+#ifdef __APPLE__
+
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <ifaddrs.h>
+
+#define ZT_ETHERTAP_IFCONFIG "/sbin/ifconfig"
+#define ZT_MAC_KEXTLOAD "/sbin/kextload"
+#define ZT_MAC_IPCONFIG "/usr/sbin/ipconfig"
+
+#endif // __APPLE__
+
namespace ZeroTier {
+// Only permit one tap to be opened concurrently across the entire process
static Mutex __tapCreateLock;
-EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
+#ifdef __LINUX__
+EthernetTap::EthernetTap(
+ const RuntimeEnvironment *renv,
+ const MAC &mac,
+ unsigned int mtu,
+ void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
+ void *arg)
throw(std::runtime_error) :
_mac(mac),
_mtu(mtu),
_r(renv),
- _putBuf((unsigned char *)0),
- _getBuf((unsigned char *)0),
- _fd(0),
- _isReading(false)
+ _handler(handler),
+ _arg(arg),
+ _fd(0)
{
char procpath[128];
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
+ if (mtu > 4096)
+ throw std::runtime_error("max tap MTU is 4096");
+
_fd = ::open("/dev/net/tun",O_RDWR);
if (_fd <= 0)
throw std::runtime_error("could not open TUN/TAP device");
@@ -152,266 +183,36 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
::close(sock);
- _putBuf = new unsigned char[((mtu + 16) * 2)];
- _getBuf = _putBuf + (mtu + 16);
+ ::pipe(_shutdownSignalPipe);
TRACE("tap %s created",_dev);
-}
-
-EthernetTap::~EthernetTap()
-{
- this->close();
- delete [] _putBuf;
-}
-
-void EthernetTap::whack()
-{
- // Linux requires nothing here
-}
-
-static bool ___removeIp(const char *_dev,std::set<InetAddress> &_ips,const InetAddress &ip)
-{
- long cpid;
- if ((cpid = (long)fork()) == 0) {
- execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
- exit(1); /* not reached unless exec fails */
- } else {
- int exitcode = 1;
- waitpid(cpid,&exitcode,0);
- if (exitcode == 0) {
- _ips.erase(ip);
- return true;
- } else return false;
- }
-}
-
-bool EthernetTap::addIP(const InetAddress &ip)
-{
- Mutex::Lock _l(_ips_m);
-
- if (!ip)
- return false;
- if (_ips.count(ip) > 0)
- return true;
-
- // Remove and reconfigure if address is the same but netmask is different
- for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
- if (i->ipsEqual(ip)) {
- ___removeIp(_dev,_ips,*i);
- break;
- }
- }
-
- int cpid;
- if ((cpid = (int)fork()) == 0) {
- execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
- exit(-1);
- } else {
- int exitcode = -1;
- waitpid(cpid,&exitcode,0);
- if (exitcode == 0) {
- _ips.insert(ip);
- return true;
- } else return false;
- }
-
- return false;
-}
-
-bool EthernetTap::removeIP(const InetAddress &ip)
-{
- Mutex::Lock _l(_ips_m);
- if (_ips.count(ip) > 0)
- return ___removeIp(_dev,_ips,ip);
- return false;
-}
-
-void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
-{
- if ((_fd > 0)&&(len <= _mtu)) {
- for(int i=0;i<6;++i)
- _putBuf[i] = to.data[i];
- for(int i=0;i<6;++i)
- _putBuf[i+6] = from.data[i];
- *((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
- memcpy(_putBuf + 14,data,len);
- ::write(_fd,_putBuf,len + 14);
- }
-}
-
-unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int &etherType,void *buf)
-{
- for(;;) {
- if (_fd > 0) {
- _isReading_m.lock();
- _isReading = true;
- _isReadingThreadId = pthread_self();
- _isReading_m.unlock();
-
- int n = (int)::read(_fd,_getBuf,_mtu + 14);
-
- _isReading_m.lock();
- _isReading = false;
- _isReading_m.unlock();
-
- if (n > 14) {
- for(int i=0;i<6;++i)
- to.data[i] = _getBuf[i];
- for(int i=0;i<6;++i)
- from.data[i] = _getBuf[i + 6];
- etherType = ntohs(((uint16_t *)_getBuf)[6]);
- n -= 14;
- memcpy(buf,_getBuf + 14,n);
- return (unsigned int)n;
- } else if (n < 0) {
- if (_fd <= 0)
- break;
- else if ((errno == EINTR)||(errno == ETIMEDOUT))
- continue;
- else {
- TRACE("unexpected error reading from tap: %s",strerror(errno));
- ::close(_fd);
- _fd = 0;
- break;
- }
- } else {
- TRACE("incomplete read from tap: %d bytes",n);
- continue;
- }
- }
- }
- return 0;
-}
-
-std::string EthernetTap::deviceName()
-{
- return std::string(_dev);
-}
-bool EthernetTap::open() const
-{
- return (_fd > 0);
+ start();
}
-
-void EthernetTap::close()
-{
- Mutex::Lock _l(__tapCreateLock); // also prevent create during close()
- if (_fd > 0) {
- int f = _fd;
- _fd = 0;
- ::close(f);
-
- _isReading_m.lock();
- if (_isReading)
- pthread_kill(_isReadingThreadId,SIGUSR2);
- _isReading_m.unlock();
- }
-}
-
-bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
-{
- char *ptr,*ptr2;
- unsigned char mac[6];
- std::set<MulticastGroup> newGroups;
-
- int fd = ::open("/proc/net/dev_mcast",O_RDONLY);
- if (fd > 0) {
- char buf[131072];
- int n = (int)::read(fd,buf,sizeof(buf));
- if ((n > 0)&&(n < (int)sizeof(buf))) {
- buf[n] = (char)0;
- for(char *l=strtok_r(buf,"\r\n",&ptr);(l);l=strtok_r((char *)0,"\r\n",&ptr)) {
- int fno = 0;
- char *devname = (char *)0;
- char *mcastmac = (char *)0;
- for(char *f=strtok_r(l," \t",&ptr2);(f);f=strtok_r((char *)0," \t",&ptr2)) {
- if (fno == 1)
- devname = f;
- else if (fno == 4)
- mcastmac = f;
- ++fno;
- }
- if ((devname)&&(!strcmp(devname,_dev))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6))
- newGroups.insert(MulticastGroup(MAC(mac),0));
- }
- }
- ::close(fd);
- }
-
- {
- Mutex::Lock _l(_ips_m);
- for(std::set<InetAddress>::const_iterator i(_ips.begin());i!=_ips.end();++i)
- newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
- }
-
- bool changed = false;
-
- newGroups.insert(_blindWildcardMulticastGroup); // always join this
-
- for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
- if (!groups.count(*mg)) {
- groups.insert(*mg);
- changed = true;
- }
- }
- for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
- if (!newGroups.count(*mg)) {
- groups.erase(mg++);
- changed = true;
- } else ++mg;
- }
-
- return changed;
-}
-
-} // namespace ZeroTier
-
-/* ======================================================================== */
-#elif defined(__APPLE__) /* ----------------------------------------------- */
-/* ======================================================================== */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <net/route.h>
-#include <net/if_dl.h>
-#include <ifaddrs.h>
-
-#define ZT_ETHERTAP_IFCONFIG "/sbin/ifconfig"
-#define ZT_MAC_KEXTLOAD "/sbin/kextload"
-#define ZT_MAC_IPCONFIG "/usr/sbin/ipconfig"
-
-namespace ZeroTier {
-
-static Mutex __tapCreateLock;
-
-EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
+#endif // __LINUX__
+
+#ifdef __APPLE__
+EthernetTap::EthernetTap(
+ const RuntimeEnvironment *renv,
+ const MAC &mac,
+ unsigned int mtu,
+ void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
+ void *arg)
throw(std::runtime_error) :
_mac(mac),
_mtu(mtu),
_r(renv),
- _putBuf((unsigned char *)0),
- _getBuf((unsigned char *)0),
- _fd(0),
- _isReading(false)
+ _handler(handler),
+ _arg(arg),
+ _fd(0)
{
char devpath[64],ethaddr[64],mtustr[16];
struct stat tmp;
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
+ if (mtu > 4096)
+ throw std::runtime_error("max tap MTU is 4096");
+
// Check for existence of ZT tap devices, try to load module if not there
if (stat("/dev/zt0",&tmp)) {
int kextpid;
@@ -453,8 +254,8 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
sprintf(mtustr,"%u",mtu);
// Configure MAC address and MTU, bring interface up
- int cpid;
- if ((cpid = (int)fork()) == 0) {
+ long cpid;
+ if ((cpid = (long)fork()) == 0) {
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
exit(-1);
} else {
@@ -468,19 +269,23 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
whack(); // turns on IPv6 on OSX
- _putBuf = new unsigned char[((mtu + 14) * 2)];
- _getBuf = _putBuf + (mtu + 14);
+ ::pipe(_shutdownSignalPipe);
+
+ start();
}
+#endif // __APPLE__
EthernetTap::~EthernetTap()
{
- this->close();
- delete [] _putBuf;
+ ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
+ join();
+ ::close(_fd);
}
+#ifdef __APPLE__
void EthernetTap::whack()
{
- int cpid = fork();
+ long cpid = (long)fork();
if (cpid == 0) {
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
exit(-1);
@@ -492,8 +297,63 @@ void EthernetTap::whack()
}
}
}
+#else
+void EthernetTap::whack() {}
+#endif // __APPLE__ / !__APPLE__
-// Helper function to actually remove IP from network device, execs ifconfig
+#ifdef __LINUX__
+static bool ___removeIp(const char *_dev,const InetAddress &ip)
+{
+ long cpid = (long)fork();
+ if (cpid == 0) {
+ execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
+ exit(1); /* not reached unless exec fails */
+ } else {
+ int exitcode = 1;
+ waitpid(cpid,&exitcode,0);
+ return (exitcode == 0);
+ }
+}
+
+bool EthernetTap::addIP(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+
+ if (!ip)
+ return false;
+ if (_ips.count(ip) > 0)
+ return true;
+
+ // Remove and reconfigure if address is the same but netmask is different
+ for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
+ if (i->ipsEqual(ip)) {
+ if (___removeIp(_dev,*i)) {
+ _ips.erase(i);
+ break;
+ } else {
+ LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str());
+ }
+ }
+ }
+
+ long cpid;
+ if ((cpid = (long)fork()) == 0) {
+ execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode == 0) {
+ _ips.insert(ip);
+ return true;
+ } else return false;
+ }
+
+ return false;
+}
+#endif // __LINUX__
+
+#ifdef __APPLE__
static bool ___removeIp(const char *_dev,const InetAddress &ip)
{
int cpid;
@@ -544,6 +404,7 @@ bool EthernetTap::addIP(const InetAddress &ip)
return false;
}
+#endif // __APPLE__
bool EthernetTap::removeIP(const InetAddress &ip)
{
@@ -559,94 +420,90 @@ bool EthernetTap::removeIP(const InetAddress &ip)
void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
+ char putBuf[4096 + 14];
if ((_fd > 0)&&(len <= _mtu)) {
for(int i=0;i<6;++i)
- _putBuf[i] = to.data[i];
+ putBuf[i] = to.data[i];
for(int i=0;i<6;++i)
- _putBuf[i+6] = from.data[i];
- *((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
- memcpy(_putBuf + 14,data,len);
+ putBuf[i+6] = from.data[i];
+ *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
+ memcpy(putBuf + 14,data,len);
len += 14;
- int n = (int)::write(_fd,_putBuf,len);
+ int n = ::write(_fd,putBuf,len);
if (n <= 0) {
LOG("error writing packet to Ethernet tap device: %s",strerror(errno));
} else if (n != (int)len) {
// Saw this gremlin once, so log it if we see it again... OSX tap
// or something seems to have goofy issues with certain MTUs.
- LOG("WARNING: Apple gremlin: tap write() wrote %d of %u bytes of frame",n,len);
+ LOG("ERROR: write underrun: %s tap write() wrote %d of %u bytes of frame",_dev,n,len);
}
}
}
-unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int &etherType,void *buf)
+std::string EthernetTap::deviceName() const
{
- for(;;) {
- if (_fd > 0) {
- _isReading_m.lock();
- _isReading = true;
- _isReadingThreadId = pthread_self();
- _isReading_m.unlock();
-
- int n = (int)::read(_fd,_getBuf,_mtu + 14);
+ return std::string(_dev);
+}
- _isReading_m.lock();
- _isReading = false;
- _isReading_m.unlock();
+#ifdef __LINUX__
+bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
+{
+ char *ptr,*ptr2;
+ unsigned char mac[6];
+ std::set<MulticastGroup> newGroups;
- if (n > 14) {
- for(int i=0;i<6;++i)
- to.data[i] = _getBuf[i];
- for(int i=0;i<6;++i)
- from.data[i] = _getBuf[i + 6];
- etherType = ntohs(((uint16_t *)_getBuf)[6]);
- n -= 14;
- memcpy(buf,_getBuf + 14,n);
- return (unsigned int)n;
- } else if (n < 0) {
- if (_fd <= 0)
- break;
- else if ((errno == EINTR)||(errno == ETIMEDOUT))
- continue;
- else {
- TRACE("unexpected error reading from tap: %s",strerror(errno));
- ::close(_fd);
- _fd = 0;
- break;
+ int fd = ::open("/proc/net/dev_mcast",O_RDONLY);
+ if (fd > 0) {
+ char buf[131072];
+ int n = (int)::read(fd,buf,sizeof(buf));
+ if ((n > 0)&&(n < (int)sizeof(buf))) {
+ buf[n] = (char)0;
+ for(char *l=strtok_r(buf,"\r\n",&ptr);(l);l=strtok_r((char *)0,"\r\n",&ptr)) {
+ int fno = 0;
+ char *devname = (char *)0;
+ char *mcastmac = (char *)0;
+ for(char *f=strtok_r(l," \t",&ptr2);(f);f=strtok_r((char *)0," \t",&ptr2)) {
+ if (fno == 1)
+ devname = f;
+ else if (fno == 4)
+ mcastmac = f;
+ ++fno;
}
- } else {
- TRACE("incomplete read from tap: %d bytes",n);
- continue;
+ if ((devname)&&(!strcmp(devname,_dev))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6))
+ newGroups.insert(MulticastGroup(MAC(mac),0));
}
}
+ ::close(fd);
}
- return 0;
-}
-std::string EthernetTap::deviceName()
-{
- return std::string(_dev);
-}
+ {
+ Mutex::Lock _l(_ips_m);
+ for(std::set<InetAddress>::const_iterator i(_ips.begin());i!=_ips.end();++i)
+ newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
+ }
-bool EthernetTap::open() const
-{
- return (_fd > 0);
-}
+ bool changed = false;
-void EthernetTap::close()
-{
- Mutex::Lock _l(__tapCreateLock); // also prevent create during close()
- if (_fd > 0) {
- int f = _fd;
- _fd = 0;
- ::close(f);
-
- _isReading_m.lock();
- if (_isReading)
- pthread_kill(_isReadingThreadId,SIGUSR2);
- _isReading_m.unlock();
+ newGroups.insert(_blindWildcardMulticastGroup); // always join this
+
+ for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
+ if (!groups.count(*mg)) {
+ groups.insert(*mg);
+ changed = true;
+ }
}
+ for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
+ if (!newGroups.count(*mg)) {
+ groups.erase(mg++);
+ changed = true;
+ } else ++mg;
+ }
+
+ return changed;
}
+#endif __LINUX__
+#ifdef __APPLE__
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
{
std::set<MulticastGroup> newGroups;
@@ -690,13 +547,54 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
return changed;
}
+#endif // __APPLE__
+
+void EthernetTap::main()
+ throw()
+{
+ fd_set readfds,nullfds;
+ MAC to,from;
+ char getBuf[4096 + 14];
+ Buffer<4096> data;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&nullfds);
+ int nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
+
+ 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)) {
+ int n = (int)::read(_fd,getBuf,_mtu + 14);
+
+ if (n > 14) {
+ for(int i=0;i<6;++i)
+ to.data[i] = (unsigned char)getBuf[i];
+ for(int i=0;i<6;++i)
+ from.data[i] = (unsigned char)getBuf[i + 6];
+ data.copyFrom(getBuf + 14,(unsigned int)n - 14);
+ _handler(_arg,from,to,ntohs(((const uint16_t *)getBuf)[6]),data);
+ } else if (n < 0) {
+ if ((errno != EINTR)&&(errno != ETIMEDOUT)) {
+ TRACE("unexpected error reading from tap: %s",strerror(errno));
+ break;
+ }
+ }
+ }
+ }
+}
} // namespace ZeroTier
-/* ======================================================================== */
-#elif defined(_WIN32) /* -------------------------------------------------- */
-/* ======================================================================== */
+#endif // __UNIX_LIKE__ //////////////////////////////////////////////////////
+
+#ifdef __WINDOWS__
+
+// TODO
-/* ======================================================================== */
-#endif
-/* ======================================================================== */
+#endif // __WINDOWS__