|
@@ -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 ðerType,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 ðerType,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__
|