diff options
Diffstat (limited to 'osdep')
-rw-r--r-- | osdep/Arp.cpp | 10 | ||||
-rw-r--r-- | osdep/Arp.hpp | 10 | ||||
-rw-r--r-- | osdep/BSDEthernetTap.cpp | 88 | ||||
-rw-r--r-- | osdep/BSDEthernetTap.hpp | 11 | ||||
-rw-r--r-- | osdep/Binder.hpp | 544 | ||||
-rw-r--r-- | osdep/BlockingQueue.hpp | 57 | ||||
-rw-r--r-- | osdep/Http.cpp | 45 | ||||
-rw-r--r-- | osdep/Http.hpp | 10 | ||||
-rw-r--r-- | osdep/LinuxEthernetTap.cpp | 109 | ||||
-rw-r--r-- | osdep/LinuxEthernetTap.hpp | 11 | ||||
-rw-r--r-- | osdep/ManagedRoute.cpp | 30 | ||||
-rw-r--r-- | osdep/ManagedRoute.hpp | 32 | ||||
-rw-r--r-- | osdep/NeighborDiscovery.cpp | 14 | ||||
-rw-r--r-- | osdep/NeighborDiscovery.hpp | 10 | ||||
-rw-r--r-- | osdep/OSUtils.cpp | 81 | ||||
-rw-r--r-- | osdep/OSUtils.hpp | 81 | ||||
-rw-r--r-- | osdep/OSXEthernetTap.cpp | 52 | ||||
-rw-r--r-- | osdep/OSXEthernetTap.hpp | 11 | ||||
-rw-r--r-- | osdep/Phy.hpp | 39 | ||||
-rw-r--r-- | osdep/PortMapper.cpp | 64 | ||||
-rw-r--r-- | osdep/PortMapper.hpp | 10 | ||||
-rw-r--r-- | osdep/TestEthernetTap.hpp | 161 | ||||
-rw-r--r-- | osdep/Thread.hpp | 54 | ||||
-rw-r--r-- | osdep/WindowsEthernetTap.cpp | 137 | ||||
-rw-r--r-- | osdep/WindowsEthernetTap.hpp | 23 |
25 files changed, 1126 insertions, 568 deletions
diff --git a/osdep/Arp.cpp b/osdep/Arp.cpp index fcc122f0..cc4e920a 100644 --- a/osdep/Arp.cpp +++ b/osdep/Arp.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdio.h> diff --git a/osdep/Arp.hpp b/osdep/Arp.hpp index 5f0d199a..27e92fdb 100644 --- a/osdep/Arp.hpp +++ b/osdep/Arp.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_ARP_HPP diff --git a/osdep/BSDEthernetTap.cpp b/osdep/BSDEthernetTap.cpp index 62fabc48..053df21d 100644 --- a/osdep/BSDEthernetTap.cpp +++ b/osdep/BSDEthernetTap.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdint.h> @@ -86,9 +94,6 @@ BSDEthernetTap::BSDEthernetTap( Mutex::Lock _gl(globalTapCreateLock); - if (mtu > 2800) - throw std::runtime_error("max tap MTU is 2800"); - #ifdef __FreeBSD__ /* FreeBSD allows long interface names and interface renaming */ @@ -109,8 +114,8 @@ BSDEthernetTap::BSDEthernetTap( std::vector<std::string> devFiles(OSUtils::listDirectory("/dev")); for(int i=9993;i<(9993+128);++i) { - Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); - Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); + OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); + OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) { long cpid = (long)vfork(); if (cpid == 0) { @@ -147,8 +152,8 @@ BSDEthernetTap::BSDEthernetTap( /* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */ for(int i=0;i<64;++i) { - Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); - Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); + OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i); + OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname); _fd = ::open(devpath,O_RDWR); if (_fd > 0) { _dev = tmpdevname; @@ -166,9 +171,9 @@ BSDEthernetTap::BSDEthernetTap( } // Configure MAC address and MTU, bring interface up - Utils::snprintf(ethaddr,sizeof(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]); - Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu); - Utils::snprintf(metstr,sizeof(metstr),"%u",_metric); + OSUtils::ztsnprintf(ethaddr,sizeof(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]); + OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu); + OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric); long cpid = (long)vfork(); if (cpid == 0) { ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0); @@ -222,7 +227,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip) { long cpid = (long)vfork(); if (cpid == 0) { - execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0); + char ipbuf[64]; + execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString(ipbuf),"-alias",(const char *)0); _exit(-1); } else if (cpid > 0) { int exitcode = -1; @@ -251,7 +257,8 @@ bool BSDEthernetTap::addIp(const InetAddress &ip) long cpid = (long)vfork(); if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0); + char tmp[128]; + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString(tmp),"alias",(const char *)0); ::_exit(-1); } else if (cpid > 0) { int exitcode = -1; @@ -313,7 +320,7 @@ std::vector<InetAddress> BSDEthernetTap::ips() const void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) { - char putBuf[4096]; + char putBuf[ZT_MAX_MTU + 64]; if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { to.copyTo(putBuf,6); from.copyTo(putBuf + 6,6); @@ -373,49 +380,22 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std: _multicastGroups.swap(newGroups); } -/* -bool BSDEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) +void BSDEthernetTap::setMtu(unsigned int mtu) { - std::set<MulticastGroup> newGroups; - struct ifmaddrs *ifmap = (struct ifmaddrs *)0; - if (!getifmaddrs(&ifmap)) { - struct ifmaddrs *p = ifmap; - while (p) { - if (p->ifma_addr->sa_family == AF_LINK) { - struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name; - struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; - if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen))) - newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0)); - } - p = p->ifma_next; + if (mtu != _mtu) { + _mtu = mtu; + long cpid = (long)vfork(); + if (cpid == 0) { + char tmp[64]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu); + execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0); + _exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + waitpid(cpid,&exitcode,0); } - freeifmaddrs(ifmap); } - - { - std::set<InetAddress> allIps(ips()); - for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i) - newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i)); - } - - bool changed = false; - - for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) { - if (!groups.count(*mg)) { - groups.insert(*mg); - changed = true; - } - } - for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) { - if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) { - groups.erase(mg++); - changed = true; - } else ++mg; - } - - return changed; } -*/ void BSDEthernetTap::threadMain() throw() @@ -423,7 +403,7 @@ void BSDEthernetTap::threadMain() fd_set readfds,nullfds; MAC to,from; int n,nfds,r; - char getBuf[8194]; + char getBuf[ZT_MAX_MTU + 64]; // Wait for a moment after startup -- wait for Network to finish // constructing itself. diff --git a/osdep/BSDEthernetTap.hpp b/osdep/BSDEthernetTap.hpp index 8c6314db..3d91dbbb 100644 --- a/osdep/BSDEthernetTap.hpp +++ b/osdep/BSDEthernetTap.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_BSDETHERNETTAP_HPP @@ -57,6 +65,7 @@ public: std::string deviceName() const; void setFriendlyName(const char *friendlyName); void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed); + void setMtu(unsigned int mtu); void threadMain() throw(); diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp index 9829f170..93fad9f1 100644 --- a/osdep/Binder.hpp +++ b/osdep/Binder.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_BINDER_HPP @@ -49,8 +57,9 @@ #include <algorithm> #include <utility> #include <map> +#include <set> +#include <atomic> -#include "../node/NonCopyable.hpp" #include "../node/InetAddress.hpp" #include "../node/Mutex.hpp" #include "../node/Utils.hpp" @@ -58,13 +67,12 @@ #include "Phy.hpp" #include "OSUtils.hpp" -/** - * Period between binder rescans/refreshes - * - * OneService also does this on detected restarts. - */ +// Period between refreshes of bindings #define ZT_BINDER_REFRESH_PERIOD 30000 +// Max number of bindings +#define ZT_BINDER_MAX_BINDINGS 256 + namespace ZeroTier { /** @@ -78,29 +86,22 @@ namespace ZeroTier { * On OSes that do not support local port enumeration or where this is not * meaningful, this degrades to binding to wildcard. */ -class Binder : NonCopyable +class Binder { private: struct _Binding { - _Binding() : - udpSock((PhySocket *)0), - tcpListenSock((PhySocket *)0), - address() {} - + _Binding() : udpSock((PhySocket *)0),tcpListenSock((PhySocket *)0) {} PhySocket *udpSock; PhySocket *tcpListenSock; InetAddress address; }; public: - Binder() {} + Binder() : _bindingCount(0) {} /** - * Close all bound ports - * - * This should be called on shutdown. It closes listen sockets and UDP ports - * but not TCP connections from any TCP listen sockets. + * Close all bound ports, should be called on shutdown * * @param phy Physical interface */ @@ -108,10 +109,11 @@ public: void closeAll(Phy<PHY_HANDLER_TYPE> &phy) { Mutex::Lock _l(_lock); - for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { - phy.close(i->udpSock,false); - phy.close(i->tcpListenSock,false); + for(unsigned int b=0,c=_bindingCount;b<c;++b) { + phy.close(_bindings[b].udpSock,false); + phy.close(_bindings[b].tcpListenSock,false); } + _bindingCount = 0; } /** @@ -121,325 +123,341 @@ public: * changes, on startup, or periodically (e.g. every 30-60s). * * @param phy Physical interface - * @param port Port to bind to on all interfaces (TCP and UDP) - * @param ignoreInterfacesByName Ignore these interfaces by name - * @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*) - * @param ignoreInterfacesByAddress Ignore these interfaces by address + * @param ports Ports to bind on all interfaces + * @param portCount Number of ports + * @param explicitBind If present, override interface IP detection and bind to these (if possible) + * @param ifChecker Interface checker function to see if an interface should be used * @tparam PHY_HANDLER_TYPE Type for Phy<> template * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method */ template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER> - void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker) + void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int *ports,unsigned int portCount,const std::vector<InetAddress> explicitBind,INTERFACE_CHECKER &ifChecker) { std::map<InetAddress,std::string> localIfAddrs; - PhySocket *udps; - //PhySocket *tcps; + PhySocket *udps,*tcps; Mutex::Lock _l(_lock); + bool interfacesEnumerated = true; + if (explicitBind.empty()) { #ifdef __WINDOWS__ - char aabuf[32768]; - ULONG aalen = sizeof(aabuf); - if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) { - PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf); - while (a) { - PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; - while (ua) { - InetAddress ip(ua->Address.lpSockaddr); - if (ifChecker.shouldBindInterface("",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())); - break; + char aabuf[32768]; + ULONG aalen = sizeof(aabuf); + if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) { + PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf); + while (a) { + PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; + while (ua) { + InetAddress ip(ua->Address.lpSockaddr); + if (ifChecker.shouldBindInterface("",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: + for(int x=0;x<(int)portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string())); + } + break; + } } + ua = ua->Next; } - ua = ua->Next; + a = a->Next; } - a = a->Next; } - } + else { + interfacesEnumerated = false; + } #else // not __WINDOWS__ - /* 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. */ + /* 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 + char fn[256],tmp[256]; + std::set<std::string> ifnames; + const unsigned long pid = (unsigned long)getpid(); + + // Get all device names + OSUtils::ztsnprintf(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); + } + else { + interfacesEnumerated = false; } - 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: - 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(devname))); + // Get IPv6 addresses (and any device names we don't already know) + OSUtils::ztsnprintf(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: + case InetAddress::IP_SCOPE_GLOBAL: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_PRIVATE: + for(int x=0;x<(int)portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname))); + } + break; + } } } } + fclose(procf); } - fclose(procf); - } - - // Get IPv4 addresses for each device - if (ifnames.size() > 0) { - const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0); - struct ifconf configuration; - configuration.ifc_len = 0; - configuration.ifc_buf = nullptr; - - if (controlfd < 0) goto ip4_address_error; - - if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; - configuration.ifc_buf = (char*)malloc(configuration.ifc_len); - - if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; - - for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) { - struct ifreq& request = configuration.ifc_req[i]; - struct sockaddr* addr = &request.ifr_ifru.ifru_addr; - if (addr->sa_family != AF_INET) continue; - std::string ifname = request.ifr_ifrn.ifrn_name; - // name can either be just interface name or interface name followed by ':' and arbitrary label - if (ifname.find(':') != std::string::npos) { - ifname = ifname.substr(0, ifname.find(':')); - } - - InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0); - if (ifChecker.shouldBindInterface(ifname.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, ifname)); - break; + // Get IPv4 addresses for each device + if (ifnames.size() > 0) { + const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0); + struct ifconf configuration; + configuration.ifc_len = 0; + configuration.ifc_buf = nullptr; + + if (controlfd < 0) goto ip4_address_error; + if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; + configuration.ifc_buf = (char*)malloc(configuration.ifc_len); + if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; + + for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) { + struct ifreq& request = configuration.ifc_req[i]; + struct sockaddr* addr = &request.ifr_ifru.ifru_addr; + if (addr->sa_family != AF_INET) continue; + std::string ifname = request.ifr_ifrn.ifrn_name; + // name can either be just interface name or interface name followed by ':' and arbitrary label + if (ifname.find(':') != std::string::npos) + ifname = ifname.substr(0, ifname.find(':')); + + InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0); + if (ifChecker.shouldBindInterface(ifname.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: + for(int x=0;x<(int)portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,ifname)); + } + break; + } } } - } - ip4_address_error: - free(configuration.ifc_buf); - if (controlfd > 0) close(controlfd); - } + ip4_address_error: + free(configuration.ifc_buf); + if (controlfd > 0) close(controlfd); + } - const bool gotViaProc = (localIfAddrs.size() > 0); + const bool gotViaProc = (localIfAddrs.size() > 0); #else - const bool gotViaProc = false; + 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; + 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: + for(int x=0;x<(int)portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name))); + } + break; + } } } + ifa = ifa->ifa_next; } - ifa = ifa->ifa_next; + freeifaddrs(ifatbl); + } + else { + interfacesEnumerated = false; } - freeifaddrs(ifatbl); } - } #endif + } else { + for(std::vector<InetAddress>::const_iterator i(explicitBind.begin());i!=explicitBind.end();++i) + localIfAddrs.insert(std::pair<InetAddress,std::string>(*i,std::string())); + } // Default to binding to wildcard if we can't enumerate addresses - if (localIfAddrs.empty()) { - localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string())); - localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string())); + if (!interfacesEnumerated && localIfAddrs.empty()) { + for(int x=0;x<(int)portCount;++x) { + localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,ports[x]),std::string())); + localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string())); + } } - // Close any old bindings to anything that doesn't exist anymore - for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) { - if (localIfAddrs.find(bi->address) == localIfAddrs.end()) { - phy.close(bi->udpSock,false); - phy.close(bi->tcpListenSock,false); + const unsigned int oldBindingCount = _bindingCount; + _bindingCount = 0; + + // Save bindings that are still valid, close those that are not + for(unsigned int b=0;b<oldBindingCount;++b) { + if (localIfAddrs.find(_bindings[b].address) != localIfAddrs.end()) { + if (_bindingCount != b) + _bindings[(unsigned int)_bindingCount] = _bindings[b]; + ++_bindingCount; + } else { + PhySocket *const udps = _bindings[b].udpSock; + PhySocket *const tcps = _bindings[b].tcpListenSock; + _bindings[b].udpSock = (PhySocket *)0; + _bindings[b].tcpListenSock = (PhySocket *)0; + phy.close(udps,false); + phy.close(tcps,false); } } - std::vector<_Binding> newBindings; + // Create new bindings for those not already bound for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) { - typename std::vector<_Binding>::const_iterator bi(_bindings.begin()); - while (bi != _bindings.end()) { - if (bi->address == ii->first) { - newBindings.push_back(*bi); + unsigned int bi = 0; + while (bi != _bindingCount) { + if (_bindings[bi].address == ii->first) break; - } ++bi; } - - if (bi == _bindings.end()) { + if (bi == _bindingCount) { udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE); - if (udps) { - //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0); - //if (tcps) { + tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0); + if ((udps)&&(tcps)) { #ifdef __LINUX__ - // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!) - if (ii->second.length() > 0) { - int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps); - char tmp[256]; - Utils::scopy(tmp,sizeof(tmp),ii->second.c_str()); - if (fd >= 0) { - if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) { - fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str()); - } - } - } + // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!) + if (ii->second.length() > 0) { + char tmp[256]; + Utils::scopy(tmp,sizeof(tmp),ii->second.c_str()); + int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps); + if (fd >= 0) + setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); + fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(tcps); + if (fd >= 0) + setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); + } #endif // __LINUX__ - newBindings.push_back(_Binding()); - newBindings.back().udpSock = udps; - //newBindings.back().tcpListenSock = tcps; - newBindings.back().address = ii->first; - //} else { - // phy.close(udps,false); - //} + if (_bindingCount < ZT_BINDER_MAX_BINDINGS) { + _bindings[_bindingCount].udpSock = udps; + _bindings[_bindingCount].tcpListenSock = tcps; + _bindings[_bindingCount].address = ii->first; + ++_bindingCount; + } + } else { + phy.close(udps,false); + phy.close(tcps,false); } } } + } - // Swapping pointers and then letting the old one fall out of scope is faster than copying again - _bindings.swap(newBindings); + /** + * @return All currently bound local interface addresses + */ + inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() const + { + std::vector<InetAddress> aa; + Mutex::Lock _l(_lock); + for(unsigned int b=0,c=_bindingCount;b<c;++b) + aa.push_back(_bindings[b].address); + return aa; } /** - * Send a UDP packet from the specified local interface, or all - * - * Unfortunately even by examining the routing table there is no ultimately - * robust way to tell where we might reach another host that works in all - * environments. As a result, we send packets with null (wildcard) local - * addresses from *every* bound interface. - * - * These are typically initial HELLOs, path probes, etc., since normal - * conversations will have a local endpoint address. So the cost is low and - * if the peer is not reachable via that route then the packet will go - * nowhere and nothing will happen. - * - * It will of course only send via interface bindings of the same socket - * family. No point in sending V4 via V6 or vice versa. - * - * In any case on most hosts there's only one or two interfaces that we - * will use, so none of this is particularly costly. - * - * @param local Local interface address or null address for 'all' - * @param remote Remote address - * @param data Data to send - * @param len Length of data - * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only) + * Send from all bound UDP sockets */ template<typename PHY_HANDLER_TYPE> - inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const + inline bool udpSendAll(Phy<PHY_HANDLER_TYPE> &phy,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl) { + bool r = false; Mutex::Lock _l(_lock); - if (local) { - for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { - if (i->address == local) { - if ((v4ttl)&&(local.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,v4ttl); - const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len); - if ((v4ttl)&&(local.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,255); - return result; - } - } - return false; - } else { - bool result = false; - for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { - if (i->address.ss_family == remote.ss_family) { - if ((v4ttl)&&(remote.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,v4ttl); - result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len); - if ((v4ttl)&&(remote.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,255); - } - } - return result; + for(unsigned int b=0,c=_bindingCount;b<c;++b) { + if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,ttl); + if (phy.udpSend(_bindings[b].udpSock,(const struct sockaddr *)addr,data,len)) r = true; + if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,255); } + return r; } /** - * @return All currently bound local interface addresses + * @param addr Address to check + * @return True if this is a bound local interface address */ - inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() + inline bool isBoundLocalInterfaceAddress(const InetAddress &addr) const { Mutex::Lock _l(_lock); - std::vector<InetAddress> aa; - for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) - aa.push_back(i->address); - return aa; + for(unsigned int b=0;b<_bindingCount;++b) { + if (_bindings[b].address == addr) + return true; + } + return false; + } + + /** + * Quickly check that a UDP socket is valid + * + * @param udpSock UDP socket to check + * @return True if socket is currently bound/allocated + */ + inline bool isUdpSocketValid(PhySocket *const udpSock) + { + for(unsigned int b=0,c=_bindingCount;b<c;++b) { + if (_bindings[b].udpSock == udpSock) + return (b < _bindingCount); // double check atomic which may have changed + } + return false; } private: - std::vector<_Binding> _bindings; + _Binding _bindings[ZT_BINDER_MAX_BINDINGS]; + std::atomic<unsigned int> _bindingCount; Mutex _lock; }; diff --git a/osdep/BlockingQueue.hpp b/osdep/BlockingQueue.hpp index 6172f4da..351a095a 100644 --- a/osdep/BlockingQueue.hpp +++ b/osdep/BlockingQueue.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_BLOCKINGQUEUE_HPP @@ -22,6 +30,7 @@ #include <queue> #include <mutex> #include <condition_variable> +#include <chrono> namespace ZeroTier { @@ -34,7 +43,7 @@ template <class T> class BlockingQueue { public: - BlockingQueue(void) {} + BlockingQueue(void) : r(true) {} inline void post(T t) { @@ -43,19 +52,53 @@ public: c.notify_one(); } - inline T get(void) + inline void stop(void) + { + std::lock_guard<std::mutex> lock(m); + r = false; + c.notify_all(); + } + + inline bool get(T &value) { std::unique_lock<std::mutex> lock(m); - while(q.empty()) + if (!r) return false; + while (q.empty()) { c.wait(lock); - T val = q.front(); + if (!r) return false; + } + value = q.front(); + q.pop(); + return true; + } + + enum TimedWaitResult + { + OK, + TIMED_OUT, + STOP + }; + + inline TimedWaitResult get(T &value,const unsigned long ms) + { + const std::chrono::milliseconds ms2{ms}; + std::unique_lock<std::mutex> lock(m); + if (!r) return STOP; + while (q.empty()) { + if (c.wait_for(lock,ms2) == std::cv_status::timeout) + return ((r) ? TIMED_OUT : STOP); + else if (!r) + return STOP; + } + value = q.front(); q.pop(); - return val; + return OK; } private: + volatile bool r; std::queue<T> q; - mutable std::mutex m; + std::mutex m; std::condition_variable c; }; diff --git a/osdep/Http.cpp b/osdep/Http.cpp index 064ccd0c..16785c96 100644 --- a/osdep/Http.cpp +++ b/osdep/Http.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdio.h> @@ -104,12 +112,12 @@ struct HttpPhyHandler inline void phyOnTcpWritable(PhySocket *sock,void **uptr) { - if (writePtr < writeSize) { - long n = phy->streamSend(sock,writeBuf + writePtr,writeSize - writePtr,true); + if (writePtr < (unsigned long)writeBuf.length()) { + long n = phy->streamSend(sock,writeBuf.data() + writePtr,(unsigned long)writeBuf.length() - writePtr,true); if (n > 0) writePtr += n; } - if (writePtr >= writeSize) + if (writePtr >= (unsigned long)writeBuf.length()) phy->setNotifyWritable(sock,false); } @@ -127,8 +135,7 @@ struct HttpPhyHandler unsigned long messageSize; unsigned long writePtr; uint64_t lastActivity; - unsigned long writeSize; - char writeBuf[32768]; + std::string writeBuf; unsigned long maxResponseSize; std::map<std::string,std::string> *responseHeaders; @@ -236,24 +243,26 @@ unsigned int Http::_do( handler.lastActivity = OSUtils::now(); try { - handler.writeSize = Utils::snprintf(handler.writeBuf,sizeof(handler.writeBuf),"%s %s HTTP/1.1\r\n",method,path); - for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) - handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"%s: %s\r\n",h->first.c_str(),h->second.c_str()); - handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"\r\n"); - if ((requestBody)&&(requestBodyLength)) { - if ((handler.writeSize + requestBodyLength) > sizeof(handler.writeBuf)) { - responseBody = "request too large"; - return 0; - } - memcpy(handler.writeBuf + handler.writeSize,requestBody,requestBodyLength); - handler.writeSize += requestBodyLength; + char tmp[1024]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path); + handler.writeBuf.append(tmp); + for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) { + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str()); + handler.writeBuf.append(tmp); } + handler.writeBuf.append("\r\n"); + if ((requestBody)&&(requestBodyLength)) + handler.writeBuf.append((const char *)requestBody,requestBodyLength); } catch ( ... ) { responseBody = "request too large"; return 0; } - handler.maxResponseSize = maxResponseSize; + if (maxResponseSize) { + handler.maxResponseSize = maxResponseSize; + } else { + handler.maxResponseSize = 2147483647; + } handler.responseHeaders = &responseHeaders; handler.responseBody = &responseBody; handler.error = false; diff --git a/osdep/Http.hpp b/osdep/Http.hpp index e7d4d03e..b717c5c9 100644 --- a/osdep/Http.hpp +++ b/osdep/Http.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_HTTP_HPP diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp index f74efc0a..06bbbada 100644 --- a/osdep/LinuxEthernetTap.cpp +++ b/osdep/LinuxEthernetTap.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdint.h> @@ -40,6 +48,7 @@ #include <algorithm> #include <utility> +#include <string> #include "../node/Constants.hpp" #include "../node/Utils.hpp" @@ -55,6 +64,19 @@ namespace ZeroTier { static Mutex __tapCreateLock; +static const char _base32_chars[32] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','2','3','4','5','6','7' }; +static void _base32_5_to_8(const uint8_t *in,char *out) +{ + out[0] = _base32_chars[(in[0]) >> 3]; + out[1] = _base32_chars[(in[0] & 0x07) << 2 | (in[1] & 0xc0) >> 6]; + out[2] = _base32_chars[(in[1] & 0x3e) >> 1]; + out[3] = _base32_chars[(in[1] & 0x01) << 4 | (in[2] & 0xf0) >> 4]; + out[4] = _base32_chars[(in[2] & 0x0f) << 1 | (in[3] & 0x80) >> 7]; + out[5] = _base32_chars[(in[3] & 0x7c) >> 2]; + out[6] = _base32_chars[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5]; + out[7] = _base32_chars[(in[4] & 0x1f)]; +} + LinuxEthernetTap::LinuxEthernetTap( const char *homePath, const MAC &mac, @@ -75,13 +97,10 @@ LinuxEthernetTap::LinuxEthernetTap( char procpath[128],nwids[32]; struct stat sbuf; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid); + OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid); Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally - if (mtu > 2800) - throw std::runtime_error("max tap MTU is 2800"); - _fd = ::open("/dev/net/tun",O_RDWR); if (_fd <= 0) { _fd = ::open("/dev/tun",O_RDWR); @@ -92,7 +111,7 @@ LinuxEthernetTap::LinuxEthernetTap( struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); - // Try to recall our last device name, or pick an unused one if that fails. + // Restore device names from legacy devicemap, but for new devices we use a base32-based canonical naming std::map<std::string,std::string> globalDeviceMap; FILE *devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"r"); if (devmapf) { @@ -115,20 +134,36 @@ LinuxEthernetTap::LinuxEthernetTap( std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids); if (gdmEntry != globalDeviceMap.end()) { Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),gdmEntry->second.c_str()); - Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); + OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); recalledDevice = (stat(procpath,&sbuf) != 0); } + if (!recalledDevice) { - int devno = 0; - do { #ifdef __SYNOLOGY__ - devno+=50; // Arbitrary number to prevent interface name conflicts - Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++); + int devno = 50; + do { + OSUtils::ztsnprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++); + OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); + } while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist #else - Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++); + uint64_t trial = 0; // incremented in the very unlikely event of a name collision with another network + do { + const uint64_t nwid40 = (nwid ^ (nwid >> 24)) + trial++; + uint8_t tmp2[5]; + char tmp3[11]; + tmp2[0] = (uint8_t)((nwid40 >> 32) & 0xff); + tmp2[1] = (uint8_t)((nwid40 >> 24) & 0xff); + tmp2[2] = (uint8_t)((nwid40 >> 16) & 0xff); + tmp2[3] = (uint8_t)((nwid40 >> 8) & 0xff); + tmp2[4] = (uint8_t)(nwid40 & 0xff); + tmp3[0] = 'z'; + tmp3[1] = 't'; + _base32_5_to_8(tmp2,tmp3 + 2); + tmp3[10] = (char)0; + memcpy(ifr.ifr_name,tmp3,11); + OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); + } while (stat(procpath,&sbuf) == 0); #endif - Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); - } while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist } ifr.ifr_flags = IFF_TAP | IFF_NO_PI; @@ -191,6 +226,7 @@ LinuxEthernetTap::LinuxEthernetTap( (void)::pipe(_shutdownSignalPipe); + /* globalDeviceMap[nwids] = _dev; devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"w"); if (devmapf) { @@ -201,6 +237,7 @@ LinuxEthernetTap::LinuxEthernetTap( } fclose(devmapf); } + */ _thread = Thread::start(this); } @@ -230,7 +267,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip) if (cpid == 0) { OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); - ::execlp("ip","ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); + char iptmp[128]; + ::execlp("ip","ip","addr","del",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0); ::_exit(-1); } else { int exitcode = -1; @@ -251,7 +289,7 @@ bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips) if (cpid == 0) { OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); - // We must know if there is at least (one) of each protocol version so we + // We must know if there is at least (one) of each protocol version so we // can properly enumerate address/netmask combinations in the ifcfg-dev file for(int i=0; i<(int)ips.size(); i++) { if (ips[i].isV4()) @@ -262,25 +300,28 @@ bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips) // Assemble and write contents of ifcfg-dev file for(int i=0; i<(int)ips.size(); i++) { if (ips[i].isV4()) { + char iptmp[64],iptmp2[64]; std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : ""; - cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString() - + "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString()+"\n"; + cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString(iptmp) + + "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString(iptmp2)+"\n"; ip4++; } else { + char iptmp[64],iptmp2[64]; std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : ""; - cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString() - + "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString()+"\n"; + cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString(iptmp) + + "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString(iptmp2)+"\n"; ip6++; } } OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length()); // Finaly, add IPs for(int i=0; i<(int)ips.size(); i++){ + char iptmp[128],iptmp2[128]; if (ips[i].isV4()) - ::execlp("ip","ip","addr","add",ips[i].toString().c_str(),"broadcast",ips[i].broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); + ::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"broadcast",ips[i].broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0); else - ::execlp("ip","ip","addr","add",ips[i].toString().c_str(),"dev",_dev.c_str(),(const char *)0); + ::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"dev",_dev.c_str(),(const char *)0); } ::_exit(-1); } else if (cpid > 0) { @@ -311,10 +352,11 @@ bool LinuxEthernetTap::addIp(const InetAddress &ip) if (cpid == 0) { OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); + char iptmp[128],iptmp2[128]; if (ip.isV4()) { - ::execlp("ip","ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); + ::execlp("ip","ip","addr","add",ip.toString(iptmp),"broadcast",ip.broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0); } else { - ::execlp("ip","ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); + ::execlp("ip","ip","addr","add",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0); } ::_exit(-1); } else if (cpid > 0) { @@ -378,7 +420,7 @@ std::vector<InetAddress> LinuxEthernetTap::ips() const void LinuxEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) { - char putBuf[8194]; + char putBuf[ZT_MAX_MTU + 64]; if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { to.copyTo(putBuf,6); from.copyTo(putBuf + 6,6); @@ -447,13 +489,28 @@ void LinuxEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,st _multicastGroups.swap(newGroups); } +void LinuxEthernetTap::setMtu(unsigned int mtu) +{ + if (_mtu != mtu) { + _mtu = mtu; + int sock = socket(AF_INET,SOCK_DGRAM,0); + if (sock > 0) { + struct ifreq ifr; + memset(&ifr,0,sizeof(ifr)); + ifr.ifr_ifru.ifru_mtu = (int)mtu; + ioctl(sock,SIOCSIFMTU,(void *)&ifr); + close(sock); + } + } +} + void LinuxEthernetTap::threadMain() throw() { fd_set readfds,nullfds; MAC to,from; int n,nfds,r; - char getBuf[8194]; + char getBuf[ZT_MAX_MTU + 64]; Thread::sleep(500); diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp index a2a00a79..5142eec1 100644 --- a/osdep/LinuxEthernetTap.hpp +++ b/osdep/LinuxEthernetTap.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_LINUXETHERNETTAP_HPP @@ -61,6 +69,7 @@ public: std::string deviceName() const; void setFriendlyName(const char *friendlyName); void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed); + void setMtu(unsigned int mtu); void threadMain() throw(); diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp index 3a020d61..d7c80704 100644 --- a/osdep/ManagedRoute.cpp +++ b/osdep/ManagedRoute.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include "../node/Constants.hpp" @@ -238,7 +246,6 @@ static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains) static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface) { - //printf("route %s %s %s %s %s\n",op,target.toString().c_str(),(via) ? via.toString().c_str() : "(null)",(ifscope) ? ifscope : "(null)",(localInterface) ? localInterface : "(null)"); long p = (long)fork(); if (p > 0) { int exitcode = -1; @@ -246,17 +253,19 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress } else if (p == 0) { ::close(STDOUT_FILENO); ::close(STDERR_FILENO); + char ttmp[64]; + char iptmp[64]; if (via) { if ((ifscope)&&(ifscope[0])) { - ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0); + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),via.toIpString(iptmp),(const char *)0); } else { - ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0); + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),via.toIpString(iptmp),(const char *)0); } } else if ((localInterface)&&(localInterface[0])) { if ((ifscope)&&(ifscope[0])) { - ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0); + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),"-interface",localInterface,(const char *)0); } else { - ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0); + ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),"-interface",localInterface,(const char *)0); } } ::_exit(-1); @@ -277,12 +286,13 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress } else if (p == 0) { ::close(STDOUT_FILENO); ::close(STDERR_FILENO); + char ipbuf[64],ipbuf2[64]; if (via) { - ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0); - ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0); + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0); + ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0); } else if ((localInterface)&&(localInterface[0])) { - ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0); - ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0); + ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0); + ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0); } ::_exit(-1); } diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp index fd77a79a..779ad6a1 100644 --- a/osdep/ManagedRoute.hpp +++ b/osdep/ManagedRoute.hpp @@ -1,3 +1,29 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + #ifndef ZT_MANAGEDROUTE_HPP #define ZT_MANAGEDROUTE_HPP @@ -8,7 +34,6 @@ #include "../node/Utils.hpp" #include "../node/SharedPtr.hpp" #include "../node/AtomicCounter.hpp" -#include "../node/NonCopyable.hpp" #include <stdexcept> #include <vector> @@ -19,7 +44,7 @@ namespace ZeroTier { /** * A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate */ -class ManagedRoute : NonCopyable +class ManagedRoute { friend class SharedPtr<ManagedRoute>; @@ -65,6 +90,9 @@ public: inline const char *device() const { return _device; } private: + ManagedRoute(const ManagedRoute &) {} + inline ManagedRoute &operator=(const ManagedRoute &) { return *this; } + InetAddress _target; InetAddress _via; InetAddress _systemVia; // for route overrides diff --git a/osdep/NeighborDiscovery.cpp b/osdep/NeighborDiscovery.cpp index 4f636310..d9862f3d 100644 --- a/osdep/NeighborDiscovery.cpp +++ b/osdep/NeighborDiscovery.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include "NeighborDiscovery.hpp" @@ -24,7 +32,7 @@ #include <assert.h> namespace ZeroTier { - + uint16_t calc_checksum (uint16_t *addr, int len) { int count = len; @@ -185,7 +193,7 @@ sockaddr_storage NeighborDiscovery::processIncomingND(const uint8_t *nd, unsigne assert(sizeof(_neighbor_advertisement) == 32); const uint64_t now = OSUtils::now(); - sockaddr_storage ip = ZT_SOCKADDR_NULL; + sockaddr_storage ip = {0}; if (len >= sizeof(_neighbor_solicitation) && nd[0] == 0x87) { // respond to Neighbor Solicitation request for local address diff --git a/osdep/NeighborDiscovery.hpp b/osdep/NeighborDiscovery.hpp index 47831bda..59186289 100644 --- a/osdep/NeighborDiscovery.hpp +++ b/osdep/NeighborDiscovery.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_NEIGHBORDISCOVERY_HPP diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index fd5efed0..cadd4e6b 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdio.h> @@ -49,6 +57,23 @@ namespace ZeroTier { +unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...) +{ + va_list ap; + + va_start(ap,fmt); + int n = (int)vsnprintf(buf,len,fmt,ap); + va_end(ap); + + if ((n >= (int)len)||(n < 0)) { + if (len) + buf[len - 1] = (char)0; + throw std::length_error("buf[] overflow"); + } + + return (unsigned int)n; +} + #ifdef __UNIX_LIKE__ bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath) throw() @@ -108,7 +133,7 @@ std::vector<std::string> OSUtils::listDirectory(const char *path,bool includeDir return r; } -long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan) +long OSUtils::cleanDirectory(const char *path,const int64_t olderThan) { long cleaned = 0; @@ -125,8 +150,8 @@ long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan) date.LowPart = ffd.ftLastWriteTime.dwLowDateTime; if (date.QuadPart > 0) { date.QuadPart -= adjust.QuadPart; - if ((uint64_t)((date.QuadPart / 10000000) * 1000) < olderThan) { - Utils::snprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName); + if ((int64_t)((date.QuadPart / 10000000) * 1000) < olderThan) { + ztsnprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName); if (DeleteFileA(tmp)) ++cleaned; } @@ -149,9 +174,9 @@ long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan) break; if (dptr) { if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&(dptr->d_type == DT_REG)) { - Utils::snprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name); + ztsnprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name); if (stat(tmp,&st) == 0) { - uint64_t mt = (uint64_t)(st.st_mtime); + int64_t mt = (int64_t)(st.st_mtime); if ((mt > 0)&&((mt * 1000) < olderThan)) { if (unlink(tmp) == 0) ++cleaned; @@ -279,7 +304,7 @@ int64_t OSUtils::getFileSize(const char *path) bool OSUtils::readFile(const char *path,std::string &buf) { - char tmp[1024]; + char tmp[16384]; FILE *f = fopen(path,"rb"); if (f) { for(;;) { @@ -355,6 +380,24 @@ std::vector<std::string> OSUtils::split(const char *s,const char *const sep,cons std::string OSUtils::platformDefaultHomePath() { +#ifdef __QNAP__ + char *cmd = "/sbin/getcfg zerotier Install_Path -f /etc/config/qpkg.conf"; + char buf[128]; + FILE *fp; + if ((fp = popen(cmd, "r")) == NULL) { + printf("Error opening pipe!\n"); + return NULL; + } + while (fgets(buf, 128, fp) != NULL) { } + if(pclose(fp)) { + printf("Command not found or exited with error status\n"); + return NULL; + } + std::string homeDir = std::string(buf); + homeDir.erase(std::remove(homeDir.begin(), homeDir.end(), '\n'), homeDir.end()); + return homeDir; +#endif + #ifdef __UNIX_LIKE__ #ifdef __APPLE__ @@ -391,7 +434,7 @@ std::string OSUtils::platformDefaultHomePath() // Inline these massive JSON operations in one place only to reduce binary footprint and compile time nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf.c_str()); } -std::string OSUtils::jsonDump(const nlohmann::json &j) { return j.dump(1); } +std::string OSUtils::jsonDump(const nlohmann::json &j,int indentation) { return j.dump(indentation); } uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl) { @@ -408,6 +451,21 @@ uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl) return dfl; } +uint64_t OSUtils::jsonIntHex(const nlohmann::json &jv,const uint64_t dfl) +{ + try { + if (jv.is_number()) { + return (uint64_t)jv; + } else if (jv.is_string()) { + std::string s = jv; + return Utils::hexStrToU64(s.c_str()); + } else if (jv.is_boolean()) { + return ((bool)jv ? 1ULL : 0ULL); + } + } catch ( ... ) {} + return dfl; +} + bool OSUtils::jsonBool(const nlohmann::json &jv,const bool dfl) { try { @@ -438,7 +496,7 @@ std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl) return jv; } else if (jv.is_number()) { char tmp[64]; - Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv); + ztsnprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv); return tmp; } else if (jv.is_boolean()) { return ((bool)jv ? std::string("1") : std::string("0")); @@ -451,9 +509,10 @@ std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv) { std::string s(jsonString(jv,"")); if (s.length() > 0) { - char *buf = new char[(s.length() / 2) + 1]; + unsigned int buflen = (unsigned int)((s.length() / 2) + 1); + char *buf = new char[buflen]; try { - unsigned int l = Utils::unhex(s,buf,(unsigned int)s.length()); + unsigned int l = Utils::unhex(s.c_str(),buf,buflen); std::string b(buf,l); delete [] buf; return b; diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp index b84d5d2d..324b7923 100644 --- a/osdep/OSUtils.hpp +++ b/osdep/OSUtils.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_OSUTILS_HPP @@ -25,7 +33,6 @@ #include <string.h> #include <time.h> -#include <string> #include <stdexcept> #include <vector> #include <map> @@ -43,6 +50,9 @@ #include <sys/time.h> #include <sys/stat.h> #include <arpa/inet.h> +#ifdef __LINUX__ +#include <sys/syscall.h> +#endif #endif #include "../ext/json/json.hpp" @@ -55,6 +65,20 @@ namespace ZeroTier { class OSUtils { public: + /** + * Variant of snprintf that is portable and throws an exception + * + * This just wraps the local implementation whatever it's called, while + * performing a few other checks and adding exceptions for overflow. + * + * @param buf Buffer to write to + * @param len Length of buffer in bytes + * @param fmt Format string + * @param ... Format arguments + * @throws std::length_error buf[] too short (buf[] will still be left null-terminated) + */ + static unsigned int ztsnprintf(char *buf,unsigned int len,const char *fmt,...); + #ifdef __UNIX_LIKE__ /** * Close STDOUT_FILENO and STDERR_FILENO and replace them with output to given path @@ -77,7 +101,6 @@ public: * @return True if delete was successful */ static inline bool rm(const char *path) - throw() { #ifdef __WINDOWS__ return (DeleteFileA(path) != FALSE); @@ -85,7 +108,7 @@ public: return (unlink(path) == 0); #endif } - static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); } + static inline bool rm(const std::string &path) { return rm(path.c_str()); } static inline bool mkdir(const char *path) { @@ -99,7 +122,17 @@ public: return true; #endif } - static inline bool mkdir(const std::string &path) throw() { return OSUtils::mkdir(path.c_str()); } + static inline bool mkdir(const std::string &path) { return OSUtils::mkdir(path.c_str()); } + + static inline bool rename(const char *o,const char *n) + { +#ifdef __WINDOWS__ + DeleteFileA(n); + return (::rename(o,n) == 0); +#else + return (::rename(o,n) == 0); +#endif + } /** * List a directory's contents @@ -118,7 +151,7 @@ public: * @param olderThan Last modified older than timestamp (ms since epoch) * @return Number of cleaned files or negative on fatal error */ - static long cleanDirectory(const char *path,const uint64_t olderThan); + static long cleanDirectory(const char *path,const int64_t olderThan); /** * Delete a directory and all its files and subdirectories recursively @@ -176,8 +209,7 @@ public: /** * @return Current time in milliseconds since epoch */ - static inline uint64_t now() - throw() + static inline int64_t now() { #ifdef __WINDOWS__ FILETIME ft; @@ -187,35 +219,17 @@ public: SystemTimeToFileTime(&st,&ft); tmp.LowPart = ft.dwLowDateTime; tmp.HighPart = ft.dwHighDateTime; - return ( ((tmp.QuadPart - 116444736000000000ULL) / 10000L) + st.wMilliseconds ); + return (int64_t)( ((tmp.QuadPart - 116444736000000000LL) / 10000L) + st.wMilliseconds ); #else struct timeval tv; - gettimeofday(&tv,(struct timezone *)0); - return ( (1000ULL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) ); -#endif - }; - - /** - * @return Current time in seconds since epoch, to the highest available resolution - */ - static inline double nowf() - throw() - { -#ifdef __WINDOWS__ - FILETIME ft; - SYSTEMTIME st; - ULARGE_INTEGER tmp; - GetSystemTime(&st); - SystemTimeToFileTime(&st,&ft); - tmp.LowPart = ft.dwLowDateTime; - tmp.HighPart = ft.dwHighDateTime; - return (((double)(tmp.QuadPart - 116444736000000000ULL)) / 10000000.0); +#ifdef __LINUX__ + syscall(SYS_gettimeofday,&tv,0); /* fix for musl libc broken gettimeofday bug */ #else - struct timeval tv; gettimeofday(&tv,(struct timezone *)0); - return ( ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0) ); #endif - } + return ( (1000LL * (int64_t)tv.tv_sec) + (int64_t)(tv.tv_usec / 1000) ); +#endif + }; /** * Read the full contents of a file into a string buffer @@ -271,8 +285,9 @@ public: static std::string platformDefaultHomePath(); static nlohmann::json jsonParse(const std::string &buf); - static std::string jsonDump(const nlohmann::json &j); + static std::string jsonDump(const nlohmann::json &j,int indentation = 1); static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl); + static uint64_t jsonIntHex(const nlohmann::json &jv,const uint64_t dfl); static bool jsonBool(const nlohmann::json &jv,const bool dfl); static std::string jsonString(const nlohmann::json &jv,const char *dfl); static std::string jsonBinFromHex(const nlohmann::json &jv); diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp index f70908b8..dbff6200 100644 --- a/osdep/OSXEthernetTap.cpp +++ b/osdep/OSXEthernetTap.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdint.h> @@ -328,10 +336,7 @@ OSXEthernetTap::OSXEthernetTap( char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32]; struct stat stattmp; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid); - - if (mtu > 2800) - throw std::runtime_error("max tap MTU is 2800"); + OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid); Mutex::Lock _gl(globalTapCreateLock); @@ -386,13 +391,13 @@ OSXEthernetTap::OSXEthernetTap( // Open the first unused tap device if we didn't recall a previous one. if (!recalledDevice) { for(int i=0;i<64;++i) { - Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i); + OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/zt%d",i); if (stat(devpath,&stattmp)) throw std::runtime_error("no more TAP devices available"); _fd = ::open(devpath,O_RDWR); if (_fd > 0) { char foo[16]; - Utils::snprintf(foo,sizeof(foo),"zt%d",i); + OSUtils::ztsnprintf(foo,sizeof(foo),"zt%d",i); _dev = foo; break; } @@ -408,9 +413,9 @@ OSXEthernetTap::OSXEthernetTap( } // Configure MAC address and MTU, bring interface up - Utils::snprintf(ethaddr,sizeof(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]); - Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu); - Utils::snprintf(metstr,sizeof(metstr),"%u",_metric); + OSUtils::ztsnprintf(ethaddr,sizeof(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]); + OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu); + OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric); long cpid = (long)vfork(); if (cpid == 0) { ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0); @@ -494,7 +499,8 @@ bool OSXEthernetTap::addIp(const InetAddress &ip) long cpid = (long)vfork(); if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0); + char tmp[128]; + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString(tmp),"alias",(const char *)0); ::_exit(-1); } else if (cpid > 0) { int exitcode = -1; @@ -514,7 +520,8 @@ bool OSXEthernetTap::removeIp(const InetAddress &ip) if (*i == ip) { long cpid = (long)vfork(); if (cpid == 0) { - execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0); + char tmp[128]; + execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString(tmp),"-alias",(const char *)0); _exit(-1); } else if (cpid > 0) { int exitcode = -1; @@ -566,7 +573,7 @@ std::vector<InetAddress> OSXEthernetTap::ips() const void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) { - char putBuf[4096]; + char putBuf[ZT_MAX_MTU + 64]; if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { to.copyTo(putBuf,6); from.copyTo(putBuf + 6,6); @@ -624,13 +631,30 @@ void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std: _multicastGroups.swap(newGroups); } +void OSXEthernetTap::setMtu(unsigned int mtu) +{ + if (mtu != _mtu) { + _mtu = mtu; + long cpid = (long)vfork(); + if (cpid == 0) { + char tmp[64]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu); + execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0); + _exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + waitpid(cpid,&exitcode,0); + } + } +} + void OSXEthernetTap::threadMain() throw() { fd_set readfds,nullfds; MAC to,from; int n,nfds,r; - char getBuf[8194]; + char getBuf[ZT_MAX_MTU + 64]; Thread::sleep(500); diff --git a/osdep/OSXEthernetTap.hpp b/osdep/OSXEthernetTap.hpp index 5a96c210..fe402901 100644 --- a/osdep/OSXEthernetTap.hpp +++ b/osdep/OSXEthernetTap.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_OSXETHERNETTAP_HPP @@ -62,6 +70,7 @@ public: std::string deviceName() const; void setFriendlyName(const char *friendlyName); void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed); + void setMtu(unsigned int mtu); void threadMain() throw(); diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index eab8a317..e359ccdd 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_PHY_HPP @@ -680,25 +688,26 @@ public: * until one works. * * @param sock Socket - * @param bufferSize Desired buffer sizes + * @param receiveBufferSize Desired size of receive buffer + * @param sendBufferSize Desired size of send buffer */ - inline void setBufferSizes(const PhySocket *sock,int bufferSize) + inline void setBufferSizes(const PhySocket *sock,int receiveBufferSize,int sendBufferSize) { PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); - if (bufferSize > 0) { - int bs = bufferSize; - while (bs >= 65536) { - int tmpbs = bs; + if (receiveBufferSize > 0) { + while (receiveBufferSize > 0) { + int tmpbs = receiveBufferSize; if (::setsockopt(sws.sock,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) break; - bs -= 16384; + receiveBufferSize -= 16384; } - bs = bufferSize; - while (bs >= 65536) { - int tmpbs = bs; + } + if (sendBufferSize > 0) { + while (sendBufferSize > 0) { + int tmpbs = sendBufferSize; if (::setsockopt(sws.sock,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) break; - bs -= 16384; + sendBufferSize -= 16384; } } } @@ -807,7 +816,7 @@ public: * @param sock Stream connection socket * @param notifyWritable Want writable notifications? */ - inline const void setNotifyWritable(PhySocket *sock,bool notifyWritable) + inline void setNotifyWritable(PhySocket *sock,bool notifyWritable) { PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); if (notifyWritable) { @@ -827,7 +836,7 @@ public: * @param sock Socket to modify * @param notifyReadable True if socket should be monitored for readability */ - inline const void setNotifyReadable(PhySocket *sock,bool notifyReadable) + inline void setNotifyReadable(PhySocket *sock,bool notifyReadable) { PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); if (notifyReadable) { @@ -957,7 +966,7 @@ public: case ZT_PHY_SOCKET_UDP: if (FD_ISSET(s->sock,&rfds)) { - for(;;) { + for(int k=0;k<1024;++k) { memset(&ss,0,sizeof(ss)); socklen_t slen = sizeof(ss); long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); diff --git a/osdep/PortMapper.cpp b/osdep/PortMapper.cpp index d3a19384..a7dd3046 100644 --- a/osdep/PortMapper.cpp +++ b/osdep/PortMapper.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifdef ZT_USE_MINIUPNPC @@ -21,6 +29,13 @@ // Uncomment to dump debug messages //#define ZT_PORTMAPPER_TRACE 1 +#ifdef __ANDROID__ +#include <android/log.h> +#define PM_TRACE(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "PortMapper", __VA_ARGS__)) +#else +#define PM_TRACE(...) fprintf(stderr, __VA_ARGS__) +#endif + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -45,15 +60,24 @@ #include <miniupnpc/miniupnpc.h> #include <miniupnpc/upnpcommands.h> #else +#ifdef __ANDROID__ +#include "miniupnpc.h" +#include "upnpcommands.h" +#else #include "../ext/miniupnpc/miniupnpc.h" #include "../ext/miniupnpc/upnpcommands.h" #endif +#endif #ifdef ZT_USE_SYSTEM_NATPMP #include <natpmp.h> #else +#ifdef __ANDROID__ +#include "natpmp.h" +#else #include "../ext/libnatpmp/natpmp.h" #endif +#endif namespace ZeroTier { @@ -75,7 +99,7 @@ public: int mode = 0; // 0 == NAT-PMP, 1 == UPnP #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: started for UDP port %d"ZT_EOL_S,localPort); + fprintf(stderr,"PortMapper: started for UDP port %d" ZT_EOL_S,localPort); #endif while (run) { @@ -99,15 +123,16 @@ public: if (initnatpmp(&natpmp,0,0) != 0) { mode = 1; + closenatpmp(&natpmp); #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: NAT-PMP: init failed, switching to UPnP mode"ZT_EOL_S); + PM_TRACE("PortMapper: NAT-PMP: init failed, switching to UPnP mode" ZT_EOL_S); #endif break; } InetAddress publicAddress; sendpublicaddressrequest(&natpmp); - uint64_t myTimeout = OSUtils::now() + 5000; + int64_t myTimeout = OSUtils::now() + 5000; do { fd_set fds; struct timeval timeout; @@ -123,7 +148,7 @@ public: publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0); } else { #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: NAT-PMP: request for external address failed, aborting..."ZT_EOL_S); + PM_TRACE("PortMapper: NAT-PMP: request for external address failed, aborting..." ZT_EOL_S); #endif closenatpmp(&natpmp); break; @@ -145,7 +170,8 @@ public: if (r == 0) { publicAddress.setPort(response.pnu.newportmapping.mappedpublicport); #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: NAT-PMP: mapped %u to %s"ZT_EOL_S,(unsigned int)localPort,publicAddress.toString().c_str()); + char paddr[128]; + PM_TRACE("PortMapper: NAT-PMP: mapped %u to %s" ZT_EOL_S,(unsigned int)localPort,publicAddress.toString(paddr)); #endif Mutex::Lock sl(surface_l); surface.clear(); @@ -162,7 +188,7 @@ public: if (!natPmpSuccess) { mode = 1; #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: NAT-PMP: request failed, switching to UPnP mode"ZT_EOL_S); + PM_TRACE("PortMapper: NAT-PMP: request failed, switching to UPnP mode" ZT_EOL_S); #endif } } @@ -187,7 +213,7 @@ public: { UPNPDev *dev = devlist; while (dev) { - fprintf(stderr,"PortMapper: found UPnP device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st); + PM_TRACE("PortMapper: found UPnP device at URL '%s': %s" ZT_EOL_S,dev->descURL,dev->st); dev = dev->pNext; } } @@ -197,22 +223,22 @@ public: memset(externalip,0,sizeof(externalip)); memset(&urls,0,sizeof(urls)); memset(&data,0,sizeof(data)); - Utils::snprintf(inport,sizeof(inport),"%d",localPort); + OSUtils::ztsnprintf(inport,sizeof(inport),"%d",localPort); if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) { #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: my LAN IP address: %s"ZT_EOL_S,lanaddr); + PM_TRACE("PortMapper: UPnP: my LAN IP address: %s" ZT_EOL_S,lanaddr); #endif if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) { #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: my external IP address: %s"ZT_EOL_S,externalip); + PM_TRACE("PortMapper: UPnP: my external IP address: %s" ZT_EOL_S,externalip); #endif for(int tries=0;tries<60;++tries) { int tryPort = (int)localPort + tries; if (tryPort >= 65535) tryPort = (tryPort - 65535) + 1025; - Utils::snprintf(outport,sizeof(outport),"%u",tryPort); + OSUtils::ztsnprintf(outport,sizeof(outport),"%u",tryPort); // First check and see if this port is already mapped to the // same unique name. If so, keep this mapping and don't try @@ -231,7 +257,7 @@ public: memset(haveLeaseDuration,0,sizeof(haveLeaseDuration)); if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) { #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: reusing previously reserved external port: %s"ZT_EOL_S,outport); + PM_TRACE("PortMapper: UPnP: reusing previously reserved external port: %s" ZT_EOL_S,outport); #endif Mutex::Lock sl(surface_l); surface.clear(); @@ -246,7 +272,7 @@ public: int mapResult = 0; if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) { #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: reserved external port: %s"ZT_EOL_S,outport); + PM_TRACE("PortMapper: UPnP: reserved external port: %s" ZT_EOL_S,outport); #endif Mutex::Lock sl(surface_l); surface.clear(); @@ -256,7 +282,7 @@ public: break; } else { #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult); + PM_TRACE("PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d" ZT_EOL_S,outport,mapResult); #endif Thread::sleep(1000); } @@ -265,13 +291,13 @@ public: } else { mode = 0; #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode"ZT_EOL_S); + PM_TRACE("PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode" ZT_EOL_S); #endif } } else { mode = 0; #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode"ZT_EOL_S); + PM_TRACE("PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode" ZT_EOL_S); #endif } @@ -280,14 +306,14 @@ public: } else { mode = 0; #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d"ZT_EOL_S,upnpError); + PM_TRACE("PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d" ZT_EOL_S,upnpError); #endif } } // --------------------------------------------------------------------- #ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY); + PM_TRACE("UPNPClient: rescanning in %d ms" ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY); #endif Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY); } diff --git a/osdep/PortMapper.hpp b/osdep/PortMapper.hpp index 0b8d15fc..fa3cdc31 100644 --- a/osdep/PortMapper.hpp +++ b/osdep/PortMapper.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifdef ZT_USE_MINIUPNPC diff --git a/osdep/TestEthernetTap.hpp b/osdep/TestEthernetTap.hpp new file mode 100644 index 00000000..6ccf92f3 --- /dev/null +++ b/osdep/TestEthernetTap.hpp @@ -0,0 +1,161 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#ifndef ZT_TESTETHERNETTAP_HPP +#define ZT_TESTETHERNETTAP_HPP + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <string> +#include <vector> +#include <stdexcept> +#include <set> + +#include "../node/Constants.hpp" +#include "../node/InetAddress.hpp" +#include "../node/MulticastGroup.hpp" +#include "../node/Mutex.hpp" +#include "../node/Utils.hpp" +#include "../osdep/OSUtils.hpp" + +namespace ZeroTier { + +/** + * Dummy test Ethernet tap that does not actually open a device on the system + */ +class TestEthernetTap +{ +public: + TestEthernetTap( + const char *homePath, + const MAC &mac, + unsigned int mtu, + unsigned int metric, + uint64_t nwid, + const char *friendlyName, + void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), + void *arg) : + _nwid(nwid), + _dev("zt_test_"), + _enabled(true) + { + char tmp[32]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)_nwid); + _dev.append(tmp); +#ifdef ZT_TEST_TAP_REPORT_TO + _reportTo.fromString(ZT_TEST_TAP_REPORT_TO); + if (_reportTo.ss_family == AF_INET) + _reportsock = socket(AF_INET,SOCK_DGRAM,0); + else if (_reportTo.ss_family == AF_INET6) + _reportsock = socket(AF_INET6,SOCK_DGRAM,0); + else _reportsock = -1; +#endif + } + + ~TestEthernetTap() + { +#ifdef ZT_TEST_TAP_REPORT_TO + if (_reportsock >= 0) + close(_reportsock); +#endif + } + + inline void setEnabled(bool en) { _enabled = en; } + inline bool enabled() const { return _enabled; } + + inline bool addIp(const InetAddress &ip) + { + Mutex::Lock _l(_lock); + _ips.insert(ip); + return true; + } + + inline bool removeIp(const InetAddress &ip) + { + Mutex::Lock _l(_lock); + _ips.erase(ip); + return true; + } + + inline std::vector<InetAddress> ips() const + { + Mutex::Lock _l(_lock); + return std::vector<InetAddress>(_ips.begin(),_ips.end()); + } + + inline void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) + { +#ifdef ZT_TEST_TAP_REPORT_TO + char tmp[10000]; + if ((_reportsock >= 0)&&(len < (sizeof(tmp) - 22))) { + const uint64_t nwid2 = Utils::hton(_nwid); + memcpy(tmp,&nwid2,8); + from.copyTo(tmp + 8,6); + to.copyTo(tmp + 14,6); + const uint16_t etherType2 = Utils::hton((uint16_t)etherType); + memcpy(tmp + 20,ðerType2,2); + memcpy(tmp + 22,data,len); + sendto(_reportsock,tmp,len + 22,0,reinterpret_cast<const struct sockaddr *>(&_reportTo),(_reportTo.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); + } +#endif + } + + inline std::string deviceName() const + { + return _dev; + } + + inline void setFriendlyName(const char *friendlyName) + { + } + + inline void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed) + { + } + + inline void setMtu(unsigned int mtu) + { + } + +private: + uint64_t _nwid; + std::string _dev; + std::set<InetAddress> _ips; + InetAddress _reportTo; +#ifdef ZT_TEST_TAP_REPORT_TO + int _reportsock; +#endif + bool _enabled; + Mutex _lock; +}; + +} // namespace ZeroTier + +#endif diff --git a/osdep/Thread.hpp b/osdep/Thread.hpp index 227c2cfe..35ea5035 100644 --- a/osdep/Thread.hpp +++ b/osdep/Thread.hpp @@ -1,6 +1,6 @@ /*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_THREAD_HPP
@@ -46,7 +54,6 @@ class Thread {
public:
Thread()
- throw()
{
_th = NULL;
_tid = 0;
@@ -54,7 +61,6 @@ public: template<typename C>
static inline Thread start(C *instance)
- throw(std::runtime_error)
{
Thread t;
t._th = CreateThread(NULL,0,&___zt_threadMain<C>,(LPVOID)instance,0,&t._tid);
@@ -84,11 +90,13 @@ public: // Not available on *nix platforms
static inline void cancelIO(const Thread &t)
{
+#if !defined(__MINGW32__) && !defined(__MINGW64__) // CancelSynchronousIo not available in MSYS2
if (t._th != NULL)
CancelSynchronousIo(t._th);
+#endif
}
- inline operator bool() const throw() { return (_th != NULL); }
+ inline operator bool() const { return (_th != NULL); }
private:
HANDLE _th;
@@ -123,33 +131,18 @@ class Thread {
public:
Thread()
- throw()
{
- memset(&_tid,0,sizeof(_tid));
- pthread_attr_init(&_tattr);
- // This corrects for systems with abnormally small defaults (musl) and also
- // shrinks the stack on systems with large defaults to save a bit of memory.
- pthread_attr_setstacksize(&_tattr,ZT_THREAD_MIN_STACK_SIZE);
- _started = false;
- }
-
- ~Thread()
- {
- pthread_attr_destroy(&_tattr);
+ memset(this,0,sizeof(Thread));
}
Thread(const Thread &t)
- throw()
{
- memcpy(&_tid,&(t._tid),sizeof(_tid));
- _started = t._started;
+ memcpy(this,&t,sizeof(Thread));
}
inline Thread &operator=(const Thread &t)
- throw()
{
- memcpy(&_tid,&(t._tid),sizeof(_tid));
- _started = t._started;
+ memcpy(this,&t,sizeof(Thread));
return *this;
}
@@ -163,12 +156,20 @@ public: */
template<typename C>
static inline Thread start(C *instance)
- throw(std::runtime_error)
{
Thread t;
- t._started = true;
- if (pthread_create(&t._tid,&t._tattr,&___zt_threadMain<C>,instance))
+ pthread_attr_t tattr;
+ pthread_attr_init(&tattr);
+ // This corrects for systems with abnormally small defaults (musl) and also
+ // shrinks the stack on systems with large defaults to save a bit of memory.
+ pthread_attr_setstacksize(&tattr,ZT_THREAD_MIN_STACK_SIZE);
+ if (pthread_create(&t._tid,&tattr,&___zt_threadMain<C>,instance)) {
+ pthread_attr_destroy(&tattr);
throw std::runtime_error("pthread_create() failed, unable to create thread");
+ } else {
+ t._started = true;
+ pthread_attr_destroy(&tattr);
+ }
return t;
}
@@ -190,11 +191,10 @@ public: */
static inline void sleep(unsigned long ms) { usleep(ms * 1000); }
- inline operator bool() const throw() { return (_started); }
+ inline operator bool() const { return (_started); }
private:
pthread_t _tid;
- pthread_attr_t _tattr;
volatile bool _started;
};
diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index 79b9d35e..aa96d33a 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdio.h> @@ -462,7 +470,9 @@ WindowsEthernetTap::WindowsEthernetTap( _arg(arg), _mac(mac), _nwid(nwid), + _mtu(mtu), _tap(INVALID_HANDLE_VALUE), + _friendlyName(friendlyName), _injectSemaphore(INVALID_HANDLE_VALUE), _pathToHelpers(hp), _run(true), @@ -473,13 +483,9 @@ WindowsEthernetTap::WindowsEthernetTap( char subkeyClass[1024]; char data[1024]; char tag[24]; - std::string mySubkeyName; - - if (mtu > 2800) - throw std::runtime_error("MTU too large."); // We "tag" registry entries with the network ID to identify persistent devices - Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid); + OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid); Mutex::Lock _l(_systemTapInitLock); @@ -522,7 +528,7 @@ WindowsEthernetTap::WindowsEthernetTap( _netCfgInstanceId = instanceId; _deviceInstanceId = instanceIdPath; - mySubkeyName = subkeyName; + _mySubkeyName = subkeyName; break; // found it! } } @@ -565,7 +571,7 @@ WindowsEthernetTap::WindowsEthernetTap( if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) _deviceInstanceId.assign(data,dataLen); - mySubkeyName = subkeyName; + _mySubkeyName = subkeyName; // Disable DHCP by default on new devices HKEY tcpIpInterfaces; @@ -596,25 +602,25 @@ WindowsEthernetTap::WindowsEthernetTap( if (_netCfgInstanceId.length() > 0) { char tmps[64]; - unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl); - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl); - tmpsl = Utils::snprintf(tmps, sizeof(tmps), "%d", mtu); - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl); + unsigned int tmpsl = OSUtils::ztsnprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1; + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl); + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl); + tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu); + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl); DWORD tmp = 0; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); tmp = IF_TYPE_ETHERNET_CSMACD; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); if (creatingNewDevice) { // Vista/2008 does not set this if (newDeviceInstanceId.length() > 0) - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length()); + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length()); // Set EnableDHCP to 0 by default on new devices tmp = 0; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); + RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); } RegCloseKey(nwAdapters); } else { @@ -717,20 +723,21 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip) DeleteUnicastIpAddressEntry(&(ipt->Table[i])); FreeMibTable(ipt); - if (ip.isV4()) { - std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); - std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); - std::string ipstr(ip.toIpString()); - for (std::vector<std::string>::iterator rip(regIps.begin()), rm(regSubnetMasks.begin()); ((rip != regIps.end()) && (rm != regSubnetMasks.end())); ++rip, ++rm) { - if (*rip == ipstr) { - regIps.erase(rip); - regSubnetMasks.erase(rm); - _setRegistryIPv4Value("IPAddress", regIps); - _setRegistryIPv4Value("SubnetMask", regSubnetMasks); - break; - } - } - } + if (ip.isV4()) { + std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); + std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); + char ipbuf[64]; + std::string ipstr(ip.toIpString(ipbuf)); + for (std::vector<std::string>::iterator rip(regIps.begin()), rm(regSubnetMasks.begin()); ((rip != regIps.end()) && (rm != regSubnetMasks.end())); ++rip, ++rm) { + if (*rip == ipstr) { + regIps.erase(rip); + regSubnetMasks.erase(rm); + _setRegistryIPv4Value("IPAddress", regIps); + _setRegistryIPv4Value("SubnetMask", regSubnetMasks); + break; + } + } + } return true; } @@ -745,7 +752,7 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip) std::vector<InetAddress> WindowsEthernetTap::ips() const { - static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it? + static const InetAddress linkLocalLoopback("fe80::1/64"); // what is this and why does Windows assign it? std::vector<InetAddress> addrs; if (!_initialized) @@ -784,12 +791,13 @@ std::vector<InetAddress> WindowsEthernetTap::ips() const void WindowsEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) { - if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > (ZT_IF_MTU))) + if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > _mtu)) return; Mutex::Lock _l(_injectPending_m); - _injectPending.push( std::pair<Array<char,ZT_IF_MTU + 32>,unsigned int>(Array<char,ZT_IF_MTU + 32>(),len + 14) ); - char *d = _injectPending.back().first.data; + _injectPending.emplace(); + _injectPending.back().len = len + 14; + char *const d = _injectPending.back().data; to.copyTo(d,6); from.copyTo(d + 6,6); d[12] = (char)((etherType >> 8) & 0xff); @@ -833,7 +841,7 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added, // pretty much anything work... IPv4, IPv6, IPX, oldskool Netbios, who knows... unsigned char mcastbuf[TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE]; DWORD bytesReturned = 0; - if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)0,0,(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) { + if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)mcastbuf,sizeof(mcastbuf),(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) { if ((bytesReturned > 0)&&(bytesReturned <= TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE)) { // sanity check MAC mac; DWORD i = 0; @@ -867,6 +875,20 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added, _multicastGroups.swap(newGroups); } +void WindowsEthernetTap::setMtu(unsigned int mtu) +{ + if (mtu != _mtu) { + _mtu = mtu; + HKEY nwAdapters; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ | KEY_WRITE, &nwAdapters) == ERROR_SUCCESS) { + char tmps[64]; + unsigned int tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu); + RegSetKeyValueA(nwAdapters, _mySubkeyName.c_str(), "MTU", REG_SZ, tmps, tmpsl); + RegCloseKey(nwAdapters); + } + } +} + NET_IFINDEX WindowsEthernetTap::interfaceIndex() const { NET_IFINDEX idx = -1; @@ -878,12 +900,12 @@ NET_IFINDEX WindowsEthernetTap::interfaceIndex() const void WindowsEthernetTap::threadMain() throw() { - char tapReadBuf[ZT_IF_MTU + 32]; + char tapReadBuf[ZT_MAX_MTU + 32]; char tapPath[128]; HANDLE wait4[3]; OVERLAPPED tapOvlRead,tapOvlWrite; - Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); + OSUtils::ztsnprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); try { while (_run) { @@ -1007,13 +1029,18 @@ void WindowsEthernetTap::threadMain() ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead); bool writeInProgress = false; ULONGLONG timeOfLastBorkCheck = GetTickCount64(); + _initialized = true; + unsigned int oldmtu = _mtu; - - _initialized = true; + setFriendlyName(_friendlyName.c_str()); while (_run) { DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE); - if (!_run) break; // will also break outer while(_run) + if (!_run) break; // will also break outer while(_run) since _run is false + + // Check for changes in MTU and break to restart tap device to reconfigure in this case + if (_mtu != oldmtu) + break; // Check for issues with adapter and close/reopen if any are detected. This // check fixes a while boatload of Windows adapter 'coma' issues after @@ -1062,7 +1089,7 @@ void WindowsEthernetTap::threadMain() } catch ( ... ) {} // handlers should not throw } } - ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead); + ReadFile(_tap,tapReadBuf,ZT_MAX_MTU + 32,NULL,&tapOvlRead); } if (writeInProgress) { @@ -1074,7 +1101,7 @@ void WindowsEthernetTap::threadMain() } else _injectPending_m.lock(); if (!_injectPending.empty()) { - WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite); + WriteFile(_tap,_injectPending.front().data,_injectPending.front().len,NULL,&tapOvlWrite); writeInProgress = true; } @@ -1204,18 +1231,18 @@ void WindowsEthernetTap::_syncIps() CreateUnicastIpAddressEntry(&ipr); } - if (aip->isV4()) - { - std::string ipStr(aip->toIpString()); - std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); - if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) { - std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); - regIps.push_back(ipStr); - regSubnetMasks.push_back(aip->netmask().toIpString()); - _setRegistryIPv4Value("IPAddress", regIps); - _setRegistryIPv4Value("SubnetMask", regSubnetMasks); - } - } + if (aip->isV4()) { + char ipbuf[64]; + std::string ipStr(aip->toIpString(ipbuf)); + std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); + if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) { + std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); + regIps.push_back(ipStr); + regSubnetMasks.push_back(aip->netmask().toIpString(ipbuf)); + _setRegistryIPv4Value("IPAddress", regIps); + _setRegistryIPv4Value("SubnetMask", regSubnetMasks); + } + } } } diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp index f2cf73f3..1e36bdd8 100644 --- a/osdep/WindowsEthernetTap.hpp +++ b/osdep/WindowsEthernetTap.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * 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 @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_WINDOWSETHERNETTAP_HPP @@ -30,7 +38,6 @@ #include "../node/Constants.hpp" #include "../node/Mutex.hpp" -#include "../node/Array.hpp" #include "../node/MulticastGroup.hpp" #include "../node/InetAddress.hpp" #include "../osdep/Thread.hpp" @@ -101,6 +108,7 @@ public: std::string deviceName() const; void setFriendlyName(const char *friendlyName); void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed); + void setMtu(unsigned int mtu); inline const NET_LUID &luid() const { return _deviceLuid; } inline const GUID &guid() const { return _deviceGuid; } @@ -122,6 +130,7 @@ private: void *_arg; MAC _mac; uint64_t _nwid; + volatile unsigned int _mtu; Thread _thread; volatile HANDLE _tap; @@ -131,13 +140,21 @@ private: NET_LUID _deviceLuid; std::string _netCfgInstanceId; std::string _deviceInstanceId; + std::string _mySubkeyName; + + std::string _friendlyName; std::vector<InetAddress> _assignedIps; // IPs assigned with addIp Mutex _assignedIps_m; std::vector<MulticastGroup> _multicastGroups; - std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending; + struct _InjectPending + { + unsigned int len; + char data[ZT_MAX_MTU + 32]; + }; + std::queue<_InjectPending> _injectPending; Mutex _injectPending_m; std::string _pathToHelpers; |