Version 0.4.3: fix Gentoo ip config failures and crashes

This version fixes problems with locating the 'ip' command on Gentoo
and possibly other Linux systems, and a problem that could cause a
crash if EthernetTap was unable to locate one of the commands it
invokes to configure IP information on tap devices.

The code also now builds on Windows. It doesn't run yet, but it's a
step. Windows port is in full swing.
This commit is contained in:
Adam Ierymenko 2013-08-13 14:42:51 -04:00
parent ce1a03bde3
commit 4ce88d7f72
2 changed files with 120 additions and 44 deletions

View File

@ -25,8 +25,10 @@
* LLC. Start here: http://www.zerotier.com/ * LLC. Start here: http://www.zerotier.com/
*/ */
#include <iostream>
#include <string> #include <string>
#include <map>
#include "Constants.hpp"
#include "EthernetTap.hpp" #include "EthernetTap.hpp"
#include "Logger.hpp" #include "Logger.hpp"
#include "RuntimeEnvironment.hpp" #include "RuntimeEnvironment.hpp"
@ -59,31 +61,81 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
#include <net/if_arp.h> #include <net/if_arp.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#ifdef __LINUX__ // Command identifiers used with command finder static (on various *nixes)
#define ZT_UNIX_IP_COMMAND 1
#define ZT_UNIX_IFCONFIG_COMMAND 2
#define ZT_MAC_KEXTLOAD_COMMAND 3
#define ZT_MAC_IPCONFIG_COMMAND 4
// Finds external commands on startup
class _CommandFinder
{
public:
_CommandFinder()
{
_findCmd(ZT_UNIX_IFCONFIG_COMMAND,"ifconfig");
#ifdef __LINUX__
_findCmd(ZT_UNIX_IP_COMMAND,"ip");
#endif
#ifdef __APPLE__
_findCmd(ZT_MAC_KEXTLOAD_COMMAND,"kextload");
_findCmd(ZT_MAC_IPCONFIG_COMMAND,"ipconfig");
#endif
}
// returns NULL if command was not found
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];
sprintf(tmp,"/sbin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
sprintf(tmp,"/usr/sbin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
sprintf(tmp,"/bin/%s",name);
if (ZeroTier::Utils::fileExists(tmp)) {
_paths[id] = tmp;
return;
}
sprintf(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;
#ifdef __LINUX__
#include <linux/if.h> #include <linux/if.h>
#include <linux/if_tun.h> #include <linux/if_tun.h>
#include <linux/if_addr.h> #include <linux/if_addr.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#define ZT_ETHERTAP_IP_COMMAND "/sbin/ip"
#define ZT_ETHERTAP_SYSCTL_COMMAND "/sbin/sysctl"
#endif // __LINUX__ #endif // __LINUX__
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <net/route.h> #include <net/route.h>
#include <net/if_dl.h> #include <net/if_dl.h>
#include <ifaddrs.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__ #endif // __APPLE__
namespace ZeroTier { namespace ZeroTier {
@ -214,14 +266,15 @@ EthernetTap::EthernetTap(
throw std::runtime_error("max tap MTU is 4096"); throw std::runtime_error("max tap MTU is 4096");
// Check for existence of ZT tap devices, try to load module if not there // Check for existence of ZT tap devices, try to load module if not there
if (stat("/dev/zt0",&tmp)) { const char *kextload = UNIX_COMMANDS[ZT_MAC_KEXTLOAD_COMMAND];
int kextpid; if ((stat("/dev/zt0",&tmp))&&(kextload)) {
long kextpid;
char tmp[4096]; char tmp[4096];
strcpy(tmp,_r->homePath.c_str()); strcpy(tmp,_r->homePath.c_str());
if ((kextpid = (int)vfork()) == 0) { if ((kextpid = (long)vfork()) == 0) {
chdir(tmp); chdir(tmp);
execl(ZT_MAC_KEXTLOAD,ZT_MAC_KEXTLOAD,"-q","-repository",tmp,"tap.kext",(const char *)0); execl(kextload,kextload,"-q","-repository",tmp,"tap.kext",(const char *)0);
exit(-1); _exit(-1);
} else { } else {
int exitcode = -1; int exitcode = -1;
waitpid(kextpid,&exitcode,0); waitpid(kextpid,&exitcode,0);
@ -250,14 +303,19 @@ EthernetTap::EthernetTap(
throw std::runtime_error("unable to set flags on file descriptor for TAP device"); throw std::runtime_error("unable to set flags on file descriptor for TAP device");
} }
sprintf(ethaddr,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); const char *ifconfig = UNIX_COMMANDS[ZT_UNIX_IFCONFIG_COMMAND];
sprintf(mtustr,"%u",mtu); if (!ifconfig) {
::close(_fd);
throw std::runtime_error("unable to find 'ifconfig' command on system");
}
// Configure MAC address and MTU, bring interface up // Configure MAC address and MTU, bring interface up
sprintf(ethaddr,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
sprintf(mtustr,"%u",mtu);
long cpid; long cpid;
if ((cpid = (long)vfork()) == 0) { if ((cpid = (long)vfork()) == 0) {
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0); execl(ifconfig,ifconfig,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
exit(-1); _exit(-1);
} else { } else {
int exitcode = -1; int exitcode = -1;
waitpid(cpid,&exitcode,0); waitpid(cpid,&exitcode,0);
@ -285,15 +343,15 @@ EthernetTap::~EthernetTap()
#ifdef __APPLE__ #ifdef __APPLE__
void EthernetTap::whack() void EthernetTap::whack()
{ {
long cpid = (long)vfork(); const char *ipconfig = UNIX_COMMANDS[ZT_MAC_IPCONFIG_COMMAND];
if (cpid == 0) { if (ipconfig) {
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0); long cpid = (long)vfork();
exit(-1); if (cpid == 0) {
} else { execl(ipconfig,ipconfig,"set",_dev,"AUTOMATIC-V6",(const char *)0);
int exitcode = -1; _exit(-1);
waitpid(cpid,&exitcode,0); } else {
if (exitcode) { int exitcode = -1;
LOG("%s: ipconfig set AUTOMATIC-V6 failed",_dev); waitpid(cpid,&exitcode,0);
} }
} }
} }
@ -304,12 +362,15 @@ void EthernetTap::whack() {}
#ifdef __LINUX__ #ifdef __LINUX__
static bool ___removeIp(const char *_dev,const InetAddress &ip) static bool ___removeIp(const char *_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(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0); execl(ipcmd,ipcmd,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
exit(1); /* not reached unless exec fails */ _exit(-1);
} else { } else {
int exitcode = 1; int exitcode = -1;
waitpid(cpid,&exitcode,0); waitpid(cpid,&exitcode,0);
return (exitcode == 0); return (exitcode == 0);
} }
@ -317,6 +378,12 @@ static bool ___removeIp(const char *_dev,const InetAddress &ip)
bool EthernetTap::addIP(const InetAddress &ip) bool EthernetTap::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);
return false;
}
Mutex::Lock _l(_ips_m); Mutex::Lock _l(_ips_m);
if (!ip) if (!ip)
@ -338,8 +405,8 @@ bool EthernetTap::addIP(const InetAddress &ip)
long cpid; long cpid;
if ((cpid = (long)vfork()) == 0) { if ((cpid = (long)vfork()) == 0) {
execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0); execl(ipcmd,ipcmd,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
exit(-1); _exit(-1);
} else { } else {
int exitcode = -1; int exitcode = -1;
waitpid(cpid,&exitcode,0); waitpid(cpid,&exitcode,0);
@ -356,10 +423,13 @@ bool EthernetTap::addIP(const InetAddress &ip)
#ifdef __APPLE__ #ifdef __APPLE__
static bool ___removeIp(const char *_dev,const InetAddress &ip) static bool ___removeIp(const char *_dev,const InetAddress &ip)
{ {
int cpid; const char *ifconfig = UNIX_COMMANDS[ZT_UNIX_IFCONFIG_COMMAND];
if ((cpid = (int)vfork()) == 0) { if (!ifconfig)
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0); return false;
exit(-1); long cpid;
if ((cpid = (long)vfork()) == 0) {
execl(ifconfig,ifconfig,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
_exit(-1);
} else { } else {
int exitcode = -1; int exitcode = -1;
waitpid(cpid,&exitcode,0); waitpid(cpid,&exitcode,0);
@ -370,6 +440,12 @@ static bool ___removeIp(const char *_dev,const InetAddress &ip)
bool EthernetTap::addIP(const InetAddress &ip) bool EthernetTap::addIP(const InetAddress &ip)
{ {
const char *ifconfig = UNIX_COMMANDS[ZT_UNIX_IFCONFIG_COMMAND];
if (!ifconfig) {
LOG("ERROR: could not configure IP address for %s: unable to find 'ifconfig' command on system (checked /sbin, /bin, /usr/sbin, /usr/bin)",_dev);
return false;
}
Mutex::Lock _l(_ips_m); Mutex::Lock _l(_ips_m);
if (!ip) if (!ip)
@ -389,10 +465,10 @@ bool EthernetTap::addIP(const InetAddress &ip)
} }
} }
int cpid; long cpid;
if ((cpid = (int)vfork()) == 0) { if ((cpid = (long)vfork()) == 0) {
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0); execl(ifconfig,ifconfig,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
exit(-1); _exit(-1);
} else { } else {
int exitcode = -1; int exitcode = -1;
waitpid(cpid,&exitcode,0); waitpid(cpid,&exitcode,0);

View File

@ -41,6 +41,6 @@
/** /**
* Revision: 16-bit (0-65535) * Revision: 16-bit (0-65535)
*/ */
#define ZEROTIER_ONE_VERSION_REVISION 2 #define ZEROTIER_ONE_VERSION_REVISION 3
#endif #endif