mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 12:33:44 +02:00
Use an alternative method for enumerating interface addresses on Linux to avoid poor performance of getifaddrs() when there are many network namespaces.
This commit is contained in:
parent
244f37179c
commit
3864a2e111
1 changed files with 118 additions and 11 deletions
129
osdep/Binder.hpp
129
osdep/Binder.hpp
|
@ -53,8 +53,10 @@
|
||||||
#include "../node/NonCopyable.hpp"
|
#include "../node/NonCopyable.hpp"
|
||||||
#include "../node/InetAddress.hpp"
|
#include "../node/InetAddress.hpp"
|
||||||
#include "../node/Mutex.hpp"
|
#include "../node/Mutex.hpp"
|
||||||
|
#include "../node/Utils.hpp"
|
||||||
|
|
||||||
#include "Phy.hpp"
|
#include "Phy.hpp"
|
||||||
|
#include "OSUtils.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Period between binder rescans/refreshes
|
* Period between binder rescans/refreshes
|
||||||
|
@ -164,14 +166,57 @@ public:
|
||||||
|
|
||||||
#else // not __WINDOWS__
|
#else // not __WINDOWS__
|
||||||
|
|
||||||
struct ifaddrs *ifatbl = (struct ifaddrs *)0;
|
/* On Linux we use an alternative method if available since getifaddrs()
|
||||||
struct ifaddrs *ifa;
|
* gets very slow when there are lots of network namespaces. This won't
|
||||||
if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
|
* work unless /proc/PID/net/if_inet6 exists and it may not on some
|
||||||
ifa = ifatbl;
|
* embedded systems, so revert to getifaddrs() there. */
|
||||||
while (ifa) {
|
|
||||||
if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
|
#ifdef __LINUX__
|
||||||
InetAddress ip = *(ifa->ifa_addr);
|
char fn[256],tmp[256];
|
||||||
if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
|
std::set<std::string> ifnames;
|
||||||
|
const unsigned long pid = (unsigned long)getpid();
|
||||||
|
|
||||||
|
// Get all device names
|
||||||
|
Utils::snprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid);
|
||||||
|
FILE *procf = fopen(fn,"r");
|
||||||
|
if (procf) {
|
||||||
|
while (fgets(tmp,sizeof(tmp),procf)) {
|
||||||
|
tmp[255] = 0;
|
||||||
|
char *saveptr = (char *)0;
|
||||||
|
for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) {
|
||||||
|
if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0))
|
||||||
|
ifnames.insert(f);
|
||||||
|
break; // we only want the first field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(procf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IPv6 addresses (and any device names we don't already know)
|
||||||
|
Utils::snprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid);
|
||||||
|
procf = fopen(fn,"r");
|
||||||
|
if (procf) {
|
||||||
|
while (fgets(tmp,sizeof(tmp),procf)) {
|
||||||
|
tmp[255] = 0;
|
||||||
|
char *saveptr = (char *)0;
|
||||||
|
unsigned char ipbits[16];
|
||||||
|
memset(ipbits,0,sizeof(ipbits));
|
||||||
|
char *devname = (char *)0;
|
||||||
|
int n = 0;
|
||||||
|
for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) {
|
||||||
|
switch(n++) {
|
||||||
|
case 0: // IP in hex
|
||||||
|
Utils::unhex(f,32,ipbits,16);
|
||||||
|
break;
|
||||||
|
case 5: // device name
|
||||||
|
devname = f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (devname) {
|
||||||
|
ifnames.insert(devname);
|
||||||
|
InetAddress ip(ipbits,16,0);
|
||||||
|
if (ifChecker.shouldBindInterface(devname,ip)) {
|
||||||
switch(ip.ipScope()) {
|
switch(ip.ipScope()) {
|
||||||
default: break;
|
default: break;
|
||||||
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||||
|
@ -179,14 +224,76 @@ public:
|
||||||
case InetAddress::IP_SCOPE_SHARED:
|
case InetAddress::IP_SCOPE_SHARED:
|
||||||
case InetAddress::IP_SCOPE_PRIVATE:
|
case InetAddress::IP_SCOPE_PRIVATE:
|
||||||
ip.setPort(port);
|
ip.setPort(port);
|
||||||
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
|
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ifa = ifa->ifa_next;
|
|
||||||
}
|
}
|
||||||
freeifaddrs(ifatbl);
|
fclose(procf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IPv4 addresses for each device
|
||||||
|
if (ifnames.size() > 0) {
|
||||||
|
const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0);
|
||||||
|
if (controlfd >= 0) {
|
||||||
|
for(std::set<std::string>::iterator devname(ifnames.begin());devname!=ifnames.end();++devname) {
|
||||||
|
struct ifreq ifr;
|
||||||
|
memset(&ifr,0,sizeof(ifr));
|
||||||
|
ifr.ifr_addr.sa_family = AF_INET;
|
||||||
|
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),devname->c_str());
|
||||||
|
if (ioctl(controlfd,SIOCGIFADDR,&ifr) >= 0) {
|
||||||
|
InetAddress ip(&(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr),4,0);
|
||||||
|
if (ifChecker.shouldBindInterface(devname->c_str(),ip)) {
|
||||||
|
switch(ip.ipScope()) {
|
||||||
|
default: break;
|
||||||
|
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||||
|
case InetAddress::IP_SCOPE_GLOBAL:
|
||||||
|
case InetAddress::IP_SCOPE_SHARED:
|
||||||
|
case InetAddress::IP_SCOPE_PRIVATE:
|
||||||
|
ip.setPort(port);
|
||||||
|
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,*devname));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(controlfd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::map<InetAddress,std::string>::iterator i(localIfAddrs.begin());i!=localIfAddrs.end();++i)
|
||||||
|
printf("%s %s\n",i->second.c_str(),i->first.toString().c_str());
|
||||||
|
const bool gotViaProc = (localIfAddrs.size() > 0);
|
||||||
|
#else
|
||||||
|
const bool gotViaProc = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!gotViaProc) {
|
||||||
|
struct ifaddrs *ifatbl = (struct ifaddrs *)0;
|
||||||
|
struct ifaddrs *ifa;
|
||||||
|
if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
|
||||||
|
ifa = ifatbl;
|
||||||
|
while (ifa) {
|
||||||
|
if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
|
||||||
|
InetAddress ip = *(ifa->ifa_addr);
|
||||||
|
if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
|
||||||
|
switch(ip.ipScope()) {
|
||||||
|
default: break;
|
||||||
|
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||||
|
case InetAddress::IP_SCOPE_GLOBAL:
|
||||||
|
case InetAddress::IP_SCOPE_SHARED:
|
||||||
|
case InetAddress::IP_SCOPE_PRIVATE:
|
||||||
|
ip.setPort(port);
|
||||||
|
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifa = ifa->ifa_next;
|
||||||
|
}
|
||||||
|
freeifaddrs(ifatbl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue