diff options
| author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-12-12 12:53:51 -0800 | 
|---|---|---|
| committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-12-12 12:53:51 -0800 | 
| commit | 3864a2e111e90c9af221fb8735849e8e4b12d3ab (patch) | |
| tree | 684a8cd4688bbb83aec4a952b688a243a4cb66f8 | |
| parent | 244f37179cb20b1ebec420da5b315ecf8ac0db40 (diff) | |
| download | infinitytier-3864a2e111e90c9af221fb8735849e8e4b12d3ab.tar.gz infinitytier-3864a2e111e90c9af221fb8735849e8e4b12d3ab.zip | |
Use an alternative method for enumerating interface addresses on Linux to avoid poor performance of getifaddrs() when there are many network namespaces.
| -rw-r--r-- | osdep/Binder.hpp | 129 | 
1 files changed, 118 insertions, 11 deletions
| diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp index 72456d38..fc18e5a5 100644 --- a/osdep/Binder.hpp +++ b/osdep/Binder.hpp @@ -53,8 +53,10 @@  #include "../node/NonCopyable.hpp"  #include "../node/InetAddress.hpp"  #include "../node/Mutex.hpp" +#include "../node/Utils.hpp"  #include "Phy.hpp" +#include "OSUtils.hpp"  /**   * Period between binder rescans/refreshes @@ -164,14 +166,57 @@ public:  #else // not __WINDOWS__ -		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)) { +		/* On Linux we use an alternative method if available since getifaddrs() +		 * gets very slow when there are lots of network namespaces. This won't +		 * work unless /proc/PID/net/if_inet6 exists and it may not on some +		 * embedded systems, so revert to getifaddrs() there. */ + +#ifdef __LINUX__ +		char fn[256],tmp[256]; +		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()) {  							default: break;  							case InetAddress::IP_SCOPE_PSEUDOPRIVATE: @@ -179,14 +224,76 @@ public:  							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))); +								localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));  								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 | 
