LinuxEthernetTap refactor.

This commit is contained in:
Adam Ierymenko 2014-08-07 20:40:23 +00:00
parent 05031ff938
commit 1f5feb75fb
3 changed files with 30 additions and 91 deletions

View file

@ -38,9 +38,8 @@ STRIP=strip --strip-all
CXXFLAGS=$(CFLAGS) -fno-rtti CXXFLAGS=$(CFLAGS) -fno-rtti
OBJS+=osnet/LinuxRoutingTable.o osnet/LinuxEthernetTap.o osnet/LinuxEthernetTapFactory.o
include objects.mk include objects.mk
OBJS+=osnet/LinuxRoutingTable.o osnet/LinuxEthernetTap.o osnet/LinuxEthernetTapFactory.o
all: one all: one
@ -58,7 +57,7 @@ installer: one FORCE
./buildinstaller.sh ./buildinstaller.sh
clean: clean:
rm -rf $(OBJS) *.o node/*.o zerotier-* build-* ZeroTierOneInstaller-* rm -rf $(OBJS) zerotier-* build-* ZeroTierOneInstaller-*
official: FORCE official: FORCE
make -j 4 ZT_OFFICIAL_RELEASE=1 make -j 4 ZT_OFFICIAL_RELEASE=1

View file

@ -37,7 +37,6 @@ STRIP=strip
CXXFLAGS=$(CFLAGS) -fno-rtti CXXFLAGS=$(CFLAGS) -fno-rtti
include objects.mk include objects.mk
OBJS+=osnet/BSDRoutingTable.o osnet/OSXEthernetTap.o osnet/OSXEthernetTapFactory.o OBJS+=osnet/BSDRoutingTable.o osnet/OSXEthernetTap.o osnet/OSXEthernetTapFactory.o
all: one all: one

View file

@ -60,67 +60,20 @@
// ff:ff:ff:ff:ff:ff with no ADI // ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
// On startup, searches the path for required external utilities (which might vary by Linux distro)
#define ZT_UNIX_IP_COMMAND 1
class _CommandFinder
{
public:
_CommandFinder()
{
_findCmd(ZT_UNIX_IP_COMMAND,"ip");
}
inline const char *operator[](int id) const
throw()
{
std::map<int,std::string>::const_iterator c(_paths.find(id));
if (c == _paths.end())
return (const char *)0;
return c->second.c_str();
}
private:
inline void _findCmd(int id,const char *name)
{
char tmp[4096];
ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/sbin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/usr/sbin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/bin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/usr/bin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
}
std::map<int,std::string> _paths;
};
static const _CommandFinder UNIX_COMMANDS;
namespace ZeroTier { namespace ZeroTier {
// Only permit one tap to be opened concurrently across the entire process
static Mutex __tapCreateLock; static Mutex __tapCreateLock;
LinuxEthernetTap::LinuxEthernetTap( LinuxEthernetTap::LinuxEthernetTap(
const RuntimeEnvironment *renv,
const char *tryToGetDevice,
const MAC &mac, const MAC &mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *desiredDevice,
const char *friendlyName,
void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
void *arg) void *arg) :
throw(std::runtime_error) : EthernetTap("LinuxEthernetTap",mac,mtu,metric),
EthernetTap("LinuxEthernetTap",mac,mtu),
_r(renv),
_handler(handler), _handler(handler),
_arg(arg), _arg(arg),
_fd(0), _fd(0),
@ -128,10 +81,11 @@ LinuxEthernetTap::LinuxEthernetTap(
{ {
char procpath[128]; char procpath[128];
struct stat sbuf; struct stat sbuf;
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
if (mtu > 4096) if (mtu > 2800)
throw std::runtime_error("max tap MTU is 4096"); throw std::runtime_error("max tap MTU is 2800");
_fd = ::open("/dev/net/tun",O_RDWR); _fd = ::open("/dev/net/tun",O_RDWR);
if (_fd <= 0) if (_fd <= 0)
@ -142,8 +96,8 @@ LinuxEthernetTap::LinuxEthernetTap(
// Try to recall our last device name, or pick an unused one if that fails. // Try to recall our last device name, or pick an unused one if that fails.
bool recalledDevice = false; bool recalledDevice = false;
if ((tryToGetDevice)&&(tryToGetDevice[0])) { if ((desiredDevice)&&(desiredDevice[0])) {
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),tryToGetDevice); Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),desiredDevice);
Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
recalledDevice = (stat(procpath,&sbuf) != 0); recalledDevice = (stat(procpath,&sbuf) != 0);
} }
@ -163,7 +117,7 @@ LinuxEthernetTap::LinuxEthernetTap(
_dev = ifr.ifr_name; _dev = ifr.ifr_name;
ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here ::ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here
// Open an arbitrary socket to talk to netlink // Open an arbitrary socket to talk to netlink
int sock = socket(AF_INET,SOCK_DGRAM,0); int sock = socket(AF_INET,SOCK_DGRAM,0);
@ -211,7 +165,7 @@ LinuxEthernetTap::LinuxEthernetTap(
::close(sock); ::close(sock);
// Set close-on-exec so that devices cannot persist if we fork/exec for update // Set close-on-exec so that devices cannot persist if we fork/exec for update
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); ::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
::pipe(_shutdownSignalPipe); ::pipe(_shutdownSignalPipe);
@ -240,53 +194,42 @@ bool LinuxEthernetTap::enabled() const
static bool ___removeIp(const std::string &_dev,const InetAddress &ip) static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
{ {
const char *ipcmd = UNIX_COMMANDS[ZT_UNIX_IP_COMMAND];
if (!ipcmd)
return false;
long cpid = (long)vfork(); long cpid = (long)vfork();
if (cpid == 0) { if (cpid == 0) {
execl(ipcmd,ipcmd,"addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); Utils::redirectUnixOutputs("/dev/null",(const char *)0);
_exit(-1); ::execl("/sbin/ip","/sbin/ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
::execl("/usr/sbin/ip","/usr/sbin/ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
::_exit(-1);
} else { } else {
int exitcode = -1; int exitcode = -1;
waitpid(cpid,&exitcode,0); ::waitpid(cpid,&exitcode,0);
return (exitcode == 0); return (exitcode == 0);
} }
} }
bool LinuxEthernetTap::addIP(const InetAddress &ip) bool LinuxEthernetTap::addIP(const InetAddress &ip)
{ {
const char *ipcmd = UNIX_COMMANDS[ZT_UNIX_IP_COMMAND];
if (!ipcmd) {
LOG("ERROR: could not configure IP address for %s: unable to find 'ip' command on system (checked /sbin, /bin, /usr/sbin, /usr/bin)",_dev.c_str());
return false;
}
if (!ip) if (!ip)
return false; return false;
std::set<InetAddress> allIps(ips()); std::set<InetAddress> allIps(ips());
if (allIps.count(ip) > 0) if (allIps.count(ip) > 0)
return true; return true;
// Remove and reconfigure if address is the same but netmask is different // Remove and reconfigure if address is the same but netmask is different
for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
if (i->ipsEqual(ip)) { if (i->ipsEqual(ip))
if (___removeIp(_dev,*i)) { ___removeIp(_dev,*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; long cpid = (long)vfork();
if ((cpid = (long)vfork()) == 0) { if (cpid == 0) {
execl(ipcmd,ipcmd,"addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); Utils::redirectUnixOutputs("/dev/null",(const char *)0);
_exit(-1); ::execl("/sbin/ip","/sbin/ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
::execl("/usr/sbin/ip","/usr/sbin/ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
::_exit(-1);
} else if (cpid > 0) { } else if (cpid > 0) {
int exitcode = -1; int exitcode = -1;
waitpid(cpid,&exitcode,0); ::waitpid(cpid,&exitcode,0);
return (exitcode == 0); return (exitcode == 0);
} }
@ -442,10 +385,8 @@ void LinuxEthernetTap::threadMain()
if (FD_ISSET(_fd,&readfds)) { if (FD_ISSET(_fd,&readfds)) {
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
if (n < 0) { if (n < 0) {
if ((errno != EINTR)&&(errno != ETIMEDOUT)) { if ((errno != EINTR)&&(errno != ETIMEDOUT))
TRACE("unexpected error reading from tap: %s",strerror(errno));
break; break;
}
} else { } else {
// Some tap drivers like to send the ethernet frame and the // Some tap drivers like to send the ethernet frame and the
// payload in two chunks, so handle that by accumulating // payload in two chunks, so handle that by accumulating