diff options
Diffstat (limited to 'node/SysEnv.cpp')
-rw-r--r-- | node/SysEnv.cpp | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/node/SysEnv.cpp b/node/SysEnv.cpp new file mode 100644 index 00000000..016e9caa --- /dev/null +++ b/node/SysEnv.cpp @@ -0,0 +1,219 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <set> +#include <string> + +#include "SysEnv.hpp" +#include "Utils.hpp" +#include "RuntimeEnvironment.hpp" +#include "NodeConfig.hpp" + +#ifdef __APPLE__ +#include <sys/sysctl.h> +#include <sys/uio.h> +#include <sys/param.h> +#include <net/route.h> +#endif + +#ifdef _WIN32 +#include <Windows.h> +#else +#include <unistd.h> +#include <signal.h> +#endif + +namespace ZeroTier { + +SysEnv::SysEnv(const RuntimeEnvironment *renv) : + _r(renv) +{ +} + +SysEnv::~SysEnv() +{ +} + +#ifdef __APPLE__ + +uint64_t SysEnv::getNetworkConfigurationFingerprint() + throw() +{ + int mib[6]; + size_t needed; + uint64_t fingerprint = 5381; // djb2 hash algorithm is used below + + // Right now this just scans for changes in default routes. This is not + // totally robust -- it will miss cases where we switch from one 10.0.0.0/24 + // network with gateway .1 to another -- but most of the time it'll pick + // up shifts in connectivity. + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_UNSPEC; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + if (!sysctl(mib,6,NULL,&needed,NULL,0)) { + char *buf = (char *)malloc(needed); + if (buf) { + if (!sysctl(mib,6,buf,&needed,NULL,0)) { + struct rt_msghdr *rtm; + for(char *next=buf,*end=buf+needed;next<end;) { + rtm = (struct rt_msghdr *)next; + char *saptr = (char *)(rtm + 1); + char *saend = next + rtm->rtm_msglen; + if (((rtm->rtm_addrs & RTA_DST))&&((rtm->rtm_addrs & RTA_GATEWAY))) { + int sano = 0; + struct sockaddr *dst = (struct sockaddr *)0; + struct sockaddr *gateway = (struct sockaddr *)0; + while (saptr < saend) { + struct sockaddr *sa = (struct sockaddr *)saptr; + if (!sa->sa_len) + break; + if (sano == 0) + dst = sa; + else if (sano == 1) + gateway = sa; + else if (sano > 1) + break; + ++sano; + saptr += sa->sa_len; + } + if ((dst)&&(gateway)) { + if ((dst->sa_family == AF_INET)&&(gateway->sa_family == AF_INET)&&(!((struct sockaddr_in *)dst)->sin_addr.s_addr)) { + fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)((struct sockaddr_in *)gateway)->sin_addr.s_addr; + } else if ((dst->sa_family == AF_INET6)&&(gateway->sa_family == AF_INET6)&&(Utils::isZero(((struct sockaddr_in6 *)dst)->sin6_addr.s6_addr,16))) { + for(unsigned int i=0;i<16;++i) + fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)((struct sockaddr_in6 *)gateway)->sin6_addr.s6_addr[i]; + } + } + } + next = saend; + } + } + free(buf); + } + } + + return fingerprint; +} + +#endif // __APPLE__ + +#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) + +uint64_t SysEnv::getNetworkConfigurationFingerprint() + throw() +{ + char buf[16384]; + uint64_t fingerprint = 5381; // djb2 hash algorithm is used below + char *t1,*t2; + + try { + std::set<std::string> tapDevs(_r->nc->networkTapDeviceNames()); + + // Include default IPv4 route if available + int fd = open("/proc/net/route",O_RDONLY); + if (fd > 0) { + long n = read(fd,buf,sizeof(buf) - 1); + ::close(fd); + if (n > 0) { + buf[n] = 0; + for(char *line=strtok_r(buf,"\r\n",&t1);(line);line=strtok_r((char *)0,"\r\n",&t1)) { + int fno = 0; + for(char *field=strtok_r(line," \t",&t2);(field);field=strtok_r((char *)0," \t",&t2)) { + if (fno == 0) { // device name + if ((tapDevs.count(std::string(field)))||(!strcmp(field,"lo"))) + break; + } else if ((fno == 1)||(fno == 2)) { // destination, gateway + if (strlen(field) == 8) { // ignore header junk, use only hex route info + while (*field) + fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)*(field++); + } + } else if (fno > 2) + break; + ++fno; + } + } + } + } + + // Include IPs of IPv6 enabled interfaces if available + fd = open("/proc/net/if_inet6",O_RDONLY); + if (fd > 0) { + long n = read(fd,buf,sizeof(buf) - 1); + ::close(fd); + if (n > 0) { + buf[n] = 0; + for(char *line=strtok_r(buf,"\r\n",&t1);(line);line=strtok_r((char *)0,"\r\n",&t1)) { + int fno = 0; + const char *v6ip = (const char *)0; + const char *devname = (const char *)0; + for(char *field=strtok_r(line," \t",&t2);(field);field=strtok_r((char *)0," \t",&t2)) { + switch(fno) { + case 0: + v6ip = field; + break; + case 5: + devname = field; + break; + } + ++fno; + } + + if ((v6ip)&&(devname)) { + if ((!(tapDevs.count(std::string(devname))))&&(strcmp(devname,"lo"))) { + while (*v6ip) + fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)*(v6ip++); + } + } + } + } + } + } catch ( ... ) {} + + return fingerprint; +} + +#endif // __linux__ + +#ifdef _WIN32 + +not implemented yet; + +#endif // _WIN32 + +} // namespace ZeroTier |