diff options
Diffstat (limited to 'osnet')
-rw-r--r-- | osnet/BSDEthernetTap.cpp | 429 | ||||
-rw-r--r-- | osnet/BSDEthernetTap.hpp | 84 | ||||
-rw-r--r-- | osnet/BSDEthernetTapFactory.cpp | 78 | ||||
-rw-r--r-- | osnet/BSDEthernetTapFactory.hpp | 63 | ||||
-rw-r--r-- | osnet/LinuxEthernetTap.cpp | 424 | ||||
-rw-r--r-- | osnet/LinuxEthernetTap.hpp | 85 | ||||
-rw-r--r-- | osnet/LinuxEthernetTapFactory.cpp | 74 | ||||
-rw-r--r-- | osnet/LinuxEthernetTapFactory.hpp | 63 | ||||
-rw-r--r-- | osnet/NativeSocketManager.cpp | 989 | ||||
-rw-r--r-- | osnet/NativeSocketManager.hpp | 117 | ||||
-rw-r--r-- | osnet/OSXEthernetTap.cpp | 639 | ||||
-rw-r--r-- | osnet/OSXEthernetTap.hpp | 91 | ||||
-rw-r--r-- | osnet/OSXEthernetTapFactory.cpp | 122 | ||||
-rw-r--r-- | osnet/OSXEthernetTapFactory.hpp | 76 | ||||
-rw-r--r-- | osnet/Phy.hpp | 817 | ||||
-rw-r--r-- | osnet/README.md | 6 | ||||
-rw-r--r-- | osnet/WindowsEthernetTap.cpp | 867 | ||||
-rw-r--r-- | osnet/WindowsEthernetTap.hpp | 115 | ||||
-rw-r--r-- | osnet/WindowsEthernetTapFactory.cpp | 162 | ||||
-rw-r--r-- | osnet/WindowsEthernetTapFactory.hpp | 90 |
20 files changed, 0 insertions, 5391 deletions
diff --git a/osnet/BSDEthernetTap.cpp b/osnet/BSDEthernetTap.cpp deleted file mode 100644 index e9899bc4..00000000 --- a/osnet/BSDEthernetTap.cpp +++ /dev/null @@ -1,429 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <unistd.h> -#include <signal.h> - -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/select.h> -#include <sys/cdefs.h> -#include <sys/uio.h> -#include <sys/param.h> -#include <sys/sysctl.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <net/if.h> -#include <ifaddrs.h> -#include <net/if_arp.h> -#include <net/if_dl.h> -#include <net/if_media.h> -#include <net/route.h> - -#include <string> -#include <map> -#include <set> -#include <algorithm> - -#include "../node/Constants.hpp" -#include "../node/Utils.hpp" -#include "../node/Mutex.hpp" -#include "BSDEthernetTap.hpp" - -#define ZT_BASE32_CHARS "0123456789abcdefghijklmnopqrstuv" - -// ff:ff:ff:ff:ff:ff with no ADI -static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); - -namespace ZeroTier { - -BSDEthernetTap::BSDEthernetTap( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) : - EthernetTap("BSDEthernetTap",mac,mtu,metric), - _handler(handler), - _arg(arg), - _mtu(mtu), - _metric(metric), - _fd(0), - _enabled(true) -{ - static Mutex globalTapCreateLock; - char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32]; - struct stat stattmp; - - // On FreeBSD at least we can rename, so use nwid to generate a deterministic unique zt#### name using base32 - // As a result we don't use desiredDevice - _dev = "zt"; - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 50) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 45) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 40) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 35) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 30) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 25) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 20) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 15) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 10) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]); - _dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]); - - Mutex::Lock _gl(globalTapCreateLock); - - if (mtu > 2800) - throw std::runtime_error("max tap MTU is 2800"); - - // On BSD we create taps and they can have high numbers, so use ones starting - // at 9993 to not conflict with other stuff. Then we rename it to zt<base32 of nwid> - std::map<std::string,bool> devFiles(Utils::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); - if (devFiles.count(std::string(tmpdevname)) == 0) { - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"create",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } else throw std::runtime_error("fork() failed"); - - if (!stat(devpath,&stattmp)) { - cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"name",_dev.c_str(),(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) - throw std::runtime_error("ifconfig rename operation failed"); - } else throw std::runtime_error("fork() failed"); - - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) - break; - else throw std::runtime_error("unable to open created tap device"); - } else { - throw std::runtime_error("cannot find /dev node for newly created tap device"); - } - } - } - - if (_fd <= 0) - throw std::runtime_error("unable to open TAP device or no more devices available"); - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - // 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); - 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); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) { - ::close(_fd); - throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); - } - } - - // Set close-on-exec so that devices cannot persist if we fork/exec for update - fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); - - ::pipe(_shutdownSignalPipe); - - _thread = Thread::start(this); -} - -BSDEthernetTap::~BSDEthernetTap() -{ - ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - Thread::join(_thread); - ::close(_fd); - ::close(_shutdownSignalPipe[0]); - ::close(_shutdownSignalPipe[1]); - - long cpid = (long)vfork(); - if (cpid == 0) { - ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - } -} - -void BSDEthernetTap::setEnabled(bool en) -{ - _enabled = en; - // TODO: interface status change -} - -bool BSDEthernetTap::enabled() const -{ - return _enabled; -} - -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); - _exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; // never reached, make compiler shut up about return value -} - -bool BSDEthernetTap::addIP(const InetAddress &ip) -{ - if (!ip) - return false; - - std::set<InetAddress> allIps(ips()); - if (allIps.count(ip) > 0) - return true; // IP/netmask already assigned - - // Remove and reconfigure if address is the same but netmask is different - for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { - if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { - if (___removeIp(_dev,*i)) - break; - } - } - - 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); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; -} - -bool BSDEthernetTap::removeIP(const InetAddress &ip) -{ - if (ips().count(ip) > 0) { - if (___removeIp(_dev,ip)) - return true; - } - return false; -} - -std::set<InetAddress> BSDEthernetTap::ips() const -{ - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (getifaddrs(&ifa)) - return std::set<InetAddress>(); - - std::set<InetAddress> r; - - struct ifaddrs *p = ifa; - while (p) { - if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { - switch(p->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; - struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; - r.insert(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); - } break; - case AF_INET6: { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; - struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; - uint32_t b[4]; - memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); - r.insert(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); - } break; - } - } - p = p->ifa_next; - } - - if (ifa) - freeifaddrs(ifa); - - return r; -} - -void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - char putBuf[4096]; - if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { - to.copyTo(putBuf,6); - from.copyTo(putBuf + 6,6); - *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); - memcpy(putBuf + 14,data,len); - len += 14; - ::write(_fd,putBuf,len); - } -} - -std::string BSDEthernetTap::deviceName() const -{ - return _dev; -} - -void BSDEthernetTap::setFriendlyName(const char *friendlyName) -{ -} - -bool BSDEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) -{ - 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; - } - 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; -} - -bool BSDEthernetTap::injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - return false; -} - -void BSDEthernetTap::threadMain() - throw() -{ - fd_set readfds,nullfds; - MAC to,from; - int n,nfds,r; - char getBuf[8194]; - Buffer<4096> data; - - // Wait for a moment after startup -- wait for Network to finish - // constructing itself. - Thread::sleep(500); - - FD_ZERO(&readfds); - FD_ZERO(&nullfds); - nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - - r = 0; - for(;;) { - FD_SET(_shutdownSignalPipe[0],&readfds); - FD_SET(_fd,&readfds); - select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - - if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread - break; - - if (FD_ISSET(_fd,&readfds)) { - n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); - if (n < 0) { - if ((errno != EINTR)&&(errno != ETIMEDOUT)) - break; - } else { - // Some tap drivers like to send the ethernet frame and the - // payload in two chunks, so handle that by accumulating - // data until we have at least a frame. - r += n; - if (r > 14) { - if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms - r = _mtu + 14; - - if (_enabled) { - to.setTo(getBuf,6); - from.setTo(getBuf + 6,6); - unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); - data.copyFrom(getBuf + 14,(unsigned int)r - 14); - _handler(_arg,from,to,etherType,data); - } - - r = 0; - } - } - } - } -} - -} // namespace ZeroTier diff --git a/osnet/BSDEthernetTap.hpp b/osnet/BSDEthernetTap.hpp deleted file mode 100644 index 582292a1..00000000 --- a/osnet/BSDEthernetTap.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_BSDETHERNETTAP_HPP -#define ZT_BSDETHERNETTAP_HPP - -#include <stdio.h> -#include <stdlib.h> - -#include <stdexcept> - -#include "../node/EthernetTap.hpp" -#include "../node/Thread.hpp" - -namespace ZeroTier { - -class BSDEthernetTap : public EthernetTap -{ -public: - BSDEthernetTap( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - - virtual ~BSDEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIP(const InetAddress &ip); - virtual bool removeIP(const InetAddress &ip); - virtual std::set<InetAddress> ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual bool updateMulticastGroups(std::set<MulticastGroup> &groups); - virtual bool injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - - void threadMain() - throw(); - -private: - void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); - void *_arg; - Thread _thread; - std::string _dev; - unsigned int _mtu; - unsigned int _metric; - int _fd; - int _shutdownSignalPipe[2]; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/BSDEthernetTapFactory.cpp b/osnet/BSDEthernetTapFactory.cpp deleted file mode 100644 index 79ae73f7..00000000 --- a/osnet/BSDEthernetTapFactory.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "BSDEthernetTapFactory.hpp" -#include "BSDEthernetTap.hpp" - -namespace ZeroTier { - -BSDEthernetTapFactory::BSDEthernetTapFactory() -{ -} - -BSDEthernetTapFactory::~BSDEthernetTapFactory() -{ - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) - delete *d; -} - -EthernetTap *BSDEthernetTapFactory::open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) -{ - Mutex::Lock _l(_devices_m); - EthernetTap *t = new BSDEthernetTap(mac,mtu,metric,nwid,desiredDevice,friendlyName,handler,arg); - _devices.push_back(t); - return t; -} - -void BSDEthernetTapFactory::close(EthernetTap *tap,bool destroyPersistentDevices) -{ - { - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) { - if (*d == tap) { - _devices.erase(d); - break; - } - } - } - delete tap; -} - -} // namespace ZeroTier diff --git a/osnet/BSDEthernetTapFactory.hpp b/osnet/BSDEthernetTapFactory.hpp deleted file mode 100644 index 63e77339..00000000 --- a/osnet/BSDEthernetTapFactory.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_BSDETHERNETTAPFACTORY_HPP -#define ZT_BSDETHERNETTAPFACTORY_HPP - -#include <vector> -#include <string> - -#include "../node/EthernetTapFactory.hpp" -#include "../node/Mutex.hpp" - -namespace ZeroTier { - -class BSDEthernetTapFactory : public EthernetTapFactory -{ -public: - BSDEthernetTapFactory(); - virtual ~BSDEthernetTapFactory(); - - virtual EthernetTap *open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - virtual void close(EthernetTap *tap,bool destroyPersistentDevices); - -private: - std::vector<EthernetTap *> _devices; - Mutex _devices_m; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/LinuxEthernetTap.cpp b/osnet/LinuxEthernetTap.cpp deleted file mode 100644 index ee89159e..00000000 --- a/osnet/LinuxEthernetTap.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/select.h> -#include <netinet/in.h> -#include <net/if_arp.h> -#include <arpa/inet.h> -#include <linux/if.h> -#include <linux/if_tun.h> -#include <linux/if_addr.h> -#include <linux/if_ether.h> -#include <ifaddrs.h> - -#include <string> -#include <map> -#include <set> -#include <algorithm> - -#include "../node/Constants.hpp" -#include "../node/Utils.hpp" -#include "../node/Mutex.hpp" -#include "LinuxEthernetTap.hpp" - -// ff:ff:ff:ff:ff:ff with no ADI -static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); - -namespace ZeroTier { - -static Mutex __tapCreateLock; - -LinuxEthernetTap::LinuxEthernetTap( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) : - EthernetTap("LinuxEthernetTap",mac,mtu,metric), - _handler(handler), - _arg(arg), - _fd(0), - _enabled(true) -{ - char procpath[128]; - struct stat sbuf; - - 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) - throw std::runtime_error(std::string("could not open TUN/TAP device: ") + strerror(errno)); - - struct ifreq ifr; - memset(&ifr,0,sizeof(ifr)); - - // Try to recall our last device name, or pick an unused one if that fails. - bool recalledDevice = false; - if ((desiredDevice)&&(desiredDevice[0])) { - Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),desiredDevice); - Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); - recalledDevice = (stat(procpath,&sbuf) != 0); - } - if (!recalledDevice) { - int devno = 0; - do { - Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++); - 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; - if (ioctl(_fd,TUNSETIFF,(void *)&ifr) < 0) { - ::close(_fd); - throw std::runtime_error("unable to configure TUN/TAP device for TAP operation"); - } - - _dev = ifr.ifr_name; - - ::ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here - - // Open an arbitrary socket to talk to netlink - int sock = socket(AF_INET,SOCK_DGRAM,0); - if (sock <= 0) { - ::close(_fd); - throw std::runtime_error("unable to open netlink socket"); - } - - // Set MAC address - ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER; - mac.copyTo(ifr.ifr_ifru.ifru_hwaddr.sa_data,6); - if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to configure TAP hardware (MAC) address"); - return; - } - - // Set MTU - ifr.ifr_ifru.ifru_mtu = (int)mtu; - if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to configure TAP MTU"); - } - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - /* Bring interface up */ - if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to get TAP interface flags"); - } - ifr.ifr_flags |= IFF_UP; - if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) { - ::close(_fd); - ::close(sock); - throw std::runtime_error("unable to set TAP interface flags"); - } - - ::close(sock); - - // Set close-on-exec so that devices cannot persist if we fork/exec for update - ::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); - - ::pipe(_shutdownSignalPipe); - - _thread = Thread::start(this); -} - -LinuxEthernetTap::~LinuxEthernetTap() -{ - ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - Thread::join(_thread); - ::close(_fd); - ::close(_shutdownSignalPipe[0]); - ::close(_shutdownSignalPipe[1]); -} - -void LinuxEthernetTap::setEnabled(bool en) -{ - _enabled = en; - // TODO: interface status change -} - -bool LinuxEthernetTap::enabled() const -{ - return _enabled; -} - -static bool ___removeIp(const std::string &_dev,const InetAddress &ip) -{ - long cpid = (long)vfork(); - if (cpid == 0) { - Utils::redirectUnixOutputs("/dev/null",(const char *)0); - ::execl("/sbin/ip","/sbin/ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::execl("/usr/sbin/ip","/usr/sbin/ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::_exit(-1); - } else { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } -} - -bool LinuxEthernetTap::addIP(const InetAddress &ip) -{ - if (!ip) - return false; - std::set<InetAddress> allIps(ips()); - if (allIps.count(ip) > 0) - return true; - - // Remove and reconfigure if address is the same but netmask is different - for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { - if (i->ipsEqual(ip)) - ___removeIp(_dev,*i); - } - - long cpid = (long)vfork(); - if (cpid == 0) { - Utils::redirectUnixOutputs("/dev/null",(const char *)0); - if (ip.isV4()) { - ::execl("/sbin/ip","/sbin/ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::execl("/usr/sbin/ip","/usr/sbin/ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0); - } else { - ::execl("/sbin/ip","/sbin/ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); - ::execl("/usr/sbin/ip","/usr/sbin/ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0); - } - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - - return false; -} - -bool LinuxEthernetTap::removeIP(const InetAddress &ip) -{ - if (ips().count(ip) > 0) { - if (___removeIp(_dev,ip)) - return true; - } - return false; -} - -std::set<InetAddress> LinuxEthernetTap::ips() const -{ - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (getifaddrs(&ifa)) - return std::set<InetAddress>(); - - std::set<InetAddress> r; - - struct ifaddrs *p = ifa; - while (p) { - if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { - switch(p->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; - struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; - r.insert(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); - } break; - case AF_INET6: { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; - struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; - uint32_t b[4]; - memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); - r.insert(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); - } break; - } - } - p = p->ifa_next; - } - - if (ifa) - freeifaddrs(ifa); - - return r; -} - -void LinuxEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - char putBuf[8194]; - if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { - to.copyTo(putBuf,6); - from.copyTo(putBuf + 6,6); - *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); - memcpy(putBuf + 14,data,len); - len += 14; - ::write(_fd,putBuf,len); - } -} - -std::string LinuxEthernetTap::deviceName() const -{ - return _dev; -} - -void LinuxEthernetTap::setFriendlyName(const char *friendlyName) -{ -} - -bool LinuxEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) -{ - char *ptr,*ptr2; - unsigned char mac[6]; - std::set<MulticastGroup> newGroups; - - int fd = ::open("/proc/net/dev_mcast",O_RDONLY); - if (fd > 0) { - char buf[131072]; - int n = (int)::read(fd,buf,sizeof(buf)); - if ((n > 0)&&(n < (int)sizeof(buf))) { - buf[n] = (char)0; - for(char *l=strtok_r(buf,"\r\n",&ptr);(l);l=strtok_r((char *)0,"\r\n",&ptr)) { - int fno = 0; - char *devname = (char *)0; - char *mcastmac = (char *)0; - for(char *f=strtok_r(l," \t",&ptr2);(f);f=strtok_r((char *)0," \t",&ptr2)) { - if (fno == 1) - devname = f; - else if (fno == 4) - mcastmac = f; - ++fno; - } - if ((devname)&&(!strcmp(devname,_dev.c_str()))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6)) - newGroups.insert(MulticastGroup(MAC(mac,6),0)); - } - } - ::close(fd); - } - - { - 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; -} - -bool LinuxEthernetTap::injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - return false; -} - -void LinuxEthernetTap::threadMain() - throw() -{ - fd_set readfds,nullfds; - MAC to,from; - int n,nfds,r; - char getBuf[8194]; - Buffer<4096> data; - - // Wait for a moment after startup -- wait for Network to finish - // constructing itself. - Thread::sleep(500); - - FD_ZERO(&readfds); - FD_ZERO(&nullfds); - nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - - r = 0; - for(;;) { - FD_SET(_shutdownSignalPipe[0],&readfds); - FD_SET(_fd,&readfds); - select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - - if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread - break; - - if (FD_ISSET(_fd,&readfds)) { - n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); - if (n < 0) { - if ((errno != EINTR)&&(errno != ETIMEDOUT)) - break; - } else { - // Some tap drivers like to send the ethernet frame and the - // payload in two chunks, so handle that by accumulating - // data until we have at least a frame. - r += n; - if (r > 14) { - if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms - r = _mtu + 14; - - if (_enabled) { - to.setTo(getBuf,6); - from.setTo(getBuf + 6,6); - unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); - data.copyFrom(getBuf + 14,(unsigned int)r - 14); - _handler(_arg,from,to,etherType,data); - } - - r = 0; - } - } - } - } -} - -} // namespace ZeroTier diff --git a/osnet/LinuxEthernetTap.hpp b/osnet/LinuxEthernetTap.hpp deleted file mode 100644 index 31980ea0..00000000 --- a/osnet/LinuxEthernetTap.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_LINUXETHERNETTAP_HPP -#define ZT_LINUXETHERNETTAP_HPP - -#include <stdio.h> -#include <stdlib.h> - -#include <stdexcept> - -#include "../node/EthernetTap.hpp" -#include "../node/Thread.hpp" - -namespace ZeroTier { - -/** - * Linux Ethernet tap using kernel tun/tap driver - */ -class LinuxEthernetTap : public EthernetTap -{ -public: - LinuxEthernetTap( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - - virtual ~LinuxEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIP(const InetAddress &ip); - virtual bool removeIP(const InetAddress &ip); - virtual std::set<InetAddress> ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual bool updateMulticastGroups(std::set<MulticastGroup> &groups); - virtual bool injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - - void threadMain() - throw(); - -private: - void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); - void *_arg; - Thread _thread; - std::string _dev; - int _fd; - int _shutdownSignalPipe[2]; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/LinuxEthernetTapFactory.cpp b/osnet/LinuxEthernetTapFactory.cpp deleted file mode 100644 index 014c6514..00000000 --- a/osnet/LinuxEthernetTapFactory.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include "LinuxEthernetTapFactory.hpp" -#include "LinuxEthernetTap.hpp" - -namespace ZeroTier { - -LinuxEthernetTapFactory::LinuxEthernetTapFactory() -{ -} - -LinuxEthernetTapFactory::~LinuxEthernetTapFactory() -{ - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) - delete *d; -} - -EthernetTap *LinuxEthernetTapFactory::open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) -{ - Mutex::Lock _l(_devices_m); - EthernetTap *t = new LinuxEthernetTap(mac,mtu,metric,nwid,desiredDevice,friendlyName,handler,arg); - _devices.push_back(t); - return t; -} - -void LinuxEthernetTapFactory::close(EthernetTap *tap,bool destroyPersistentDevices) -{ - { - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) { - if (*d == tap) { - _devices.erase(d); - break; - } - } - } - delete tap; -} - -} // namespace ZeroTier diff --git a/osnet/LinuxEthernetTapFactory.hpp b/osnet/LinuxEthernetTapFactory.hpp deleted file mode 100644 index e61863ed..00000000 --- a/osnet/LinuxEthernetTapFactory.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_LINUXETHERNETTAPFACTORY_HPP -#define ZT_LINUXETHERNETTAPFACTORY_HPP - -#include <vector> -#include <string> - -#include "../node/EthernetTapFactory.hpp" -#include "../node/Mutex.hpp" - -namespace ZeroTier { - -class LinuxEthernetTapFactory : public EthernetTapFactory -{ -public: - LinuxEthernetTapFactory(); - virtual ~LinuxEthernetTapFactory(); - - virtual EthernetTap *open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - virtual void close(EthernetTap *tap,bool destroyPersistentDevices); - -private: - std::vector<EthernetTap *> _devices; - Mutex _devices_m; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/NativeSocketManager.cpp b/osnet/NativeSocketManager.cpp deleted file mode 100644 index 797764ef..00000000 --- a/osnet/NativeSocketManager.cpp +++ /dev/null @@ -1,989 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -/* Native SocketManager for Windows and Unix */ - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <time.h> -#include <sys/types.h> - -#include <algorithm> - -#include "../node/Constants.hpp" -#include "NativeSocketManager.hpp" - -#ifndef __WINDOWS__ -#include <errno.h> -#include <unistd.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <signal.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#endif // !__WINDOWS__ - -// Uncomment to turn off TCP Nagle -//#define ZT_TCP_NODELAY - -// Allow us to use the same value on Windows and *nix -#ifndef INVALID_SOCKET -#define INVALID_SOCKET (-1) -#endif - -#ifdef __WINDOWS__ -#define CLOSE_SOCKET(s) ::closesocket(s) -#else -#define CLOSE_SOCKET(s) ::close(s) -#endif - -namespace ZeroTier { - -////////////////////////////////////////////////////////////////////////////// -// Socket implementations -////////////////////////////////////////////////////////////////////////////// - -class NativeSocket : public Socket -{ -public: -#ifdef __WINDOWS__ - NativeSocket(const Type &t,SOCKET s) : Socket(t),_sock(s) {} - SOCKET _sock; -#else - NativeSocket(const Type &t,int s) : Socket(t),_sock(s) {} - int _sock; -#endif -}; - -/** - * Native UDP socket - */ -class NativeUdpSocket : public NativeSocket -{ -public: -#ifdef __WINDOWS__ - NativeUdpSocket(Type t,SOCKET s) : NativeSocket(t,s) {} -#else - NativeUdpSocket(Type t,int s) : NativeSocket(t,s) {} -#endif - - virtual ~NativeUdpSocket() - { -#ifdef __WINDOWS__ - ::closesocket(_sock); -#else - ::close(_sock); -#endif - } - - virtual bool send(const InetAddress &to,const void *msg,unsigned int msglen) - { - if (to.isV6()) { -#ifdef __WINDOWS__ - return ((int)sendto(_sock,(const char *)msg,msglen,0,to.saddr(),to.saddrLen()) == (int)msglen); -#else - return ((int)sendto(_sock,msg,msglen,0,to.saddr(),to.saddrLen()) == (int)msglen); -#endif - } else { -#ifdef __WINDOWS__ - return ((int)sendto(_sock,(const char *)msg,msglen,0,to.saddr(),to.saddrLen()) == (int)msglen); -#else - return ((int)sendto(_sock,msg,msglen,0,to.saddr(),to.saddrLen()) == (int)msglen); -#endif - } - } - - inline bool notifyAvailableForRead(const SharedPtr<Socket> &self,NativeSocketManager *sm,void (*handler)(const SharedPtr<Socket> &,void *,const InetAddress &,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &),void *arg) - { - Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> buf; - InetAddress from; - socklen_t salen = from.saddrSpaceLen(); - int n = (int)recvfrom(_sock,(char *)(buf.data()),ZT_SOCKET_MAX_MESSAGE_LEN,0,from.saddr(),&salen); - if (n > 0) { - buf.setSize((unsigned int)n); - try { - handler(self,arg,from,buf); - } catch ( ... ) {} // handlers should not throw - } - return true; - } - - inline bool notifyAvailableForWrite(const SharedPtr<Socket> &self,NativeSocketManager *sm) - { - return true; - } -}; - -/** - * A TCP socket encapsulating ZeroTier packets over a TCP stream connection - * - * This implements a simple packet encapsulation that is designed to look like - * a TLS connection. It's not a TLS connection, but it sends TLS format record - * headers. It could be extended in the future to implement a fake TLS - * handshake. - * - * At the moment, each packet is just made to look like TLS application data: - * <[1] TLS content type> - currently 0x17 for "application data" - * <[1] TLS major version> - currently 0x03 for TLS 1.2 - * <[1] TLS minor version> - currently 0x03 for TLS 1.2 - * <[2] payload length> - 16-bit length of payload in bytes - * <[...] payload> - Message payload - * - * The primary purpose of TCP sockets is to work over ports like HTTPS(443), - * allowing users behind particularly fascist firewalls to at least reach - * ZeroTier's supernodes. UDP is the preferred method of communication as - * encapsulating L2 and L3 protocols over TCP is inherently inefficient - * due to double-ACKs. So TCP is only used as a fallback. - */ -class NativeTcpSocket : public NativeSocket -{ -public: -#ifdef __WINDOWS__ - NativeTcpSocket(NativeSocketManager *sm,SOCKET s,Socket::Type t,bool c,const InetAddress &r) : -#else - NativeTcpSocket(NativeSocketManager *sm,int s,Socket::Type t,bool c,const InetAddress &r) : -#endif - NativeSocket(t,s), - _lastActivity(Utils::now()), - _sm(sm), - _inptr(0), - _outptr(0), - _connecting(c), - _remote(r) {} - - virtual ~NativeTcpSocket() - { -#ifdef __WINDOWS__ - ::closesocket(_sock); -#else - ::close(_sock); -#endif - } - - virtual bool send(const InetAddress &to,const void *msg,unsigned int msglen) - { - if (msglen > ZT_SOCKET_MAX_MESSAGE_LEN) - return false; // message too big - if (!msglen) - return true; // sanity check - - Mutex::Lock _l(_writeLock); - - bool writeInProgress = ((_outptr != 0)||(_connecting)); - - if ((_outptr + 5 + msglen) > (unsigned int)sizeof(_outbuf)) - return false; - - _outbuf[_outptr++] = 0x17; // look like TLS data - _outbuf[_outptr++] = 0x03; - _outbuf[_outptr++] = 0x03; // look like TLS 1.2 - _outbuf[_outptr++] = (unsigned char)((msglen >> 8) & 0xff); - _outbuf[_outptr++] = (unsigned char)(msglen & 0xff); - for(unsigned int i=0;i<msglen;++i) - _outbuf[_outptr++] = ((const unsigned char *)msg)[i]; - - if (!writeInProgress) { - // If no output was enqueued before this, try to send() it and then - // start a queued write if any remains after that. - - int n = (int)::send(_sock,(const char *)_outbuf,_outptr,0); - if (n > 0) - memmove(_outbuf,_outbuf + (unsigned int)n,_outptr -= (unsigned int)n); - - if (_outptr) { - _sm->_startNotifyWrite(this); - _sm->whack(); - } - } // else just leave in _outbuf[] to get written when stream is available for write - - return true; - } - - inline bool notifyAvailableForRead(const SharedPtr<Socket> &self,NativeSocketManager *sm,void (*handler)(const SharedPtr<Socket> &,void *,const InetAddress &,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &),void *arg) - { - unsigned char buf[65536]; - - int n = (int)::recv(_sock,(char *)buf,sizeof(buf),0); - if (n <= 0) - return false; // read error, stream probably closed - - unsigned int p = _inptr,pl = 0; - for(int k=0;k<n;++k) { - _inbuf[p++] = buf[k]; - if (p >= (int)sizeof(_inbuf)) - return false; // read overrun, packet too large or invalid - - if ((!pl)&&(p >= 5)) { - if (_inbuf[0] == 0x17) { - // fake TLS data frame, next two bytes are TLS version and are ignored - pl = (((unsigned int)_inbuf[3] << 8) | (unsigned int)_inbuf[4]) + 5; - } else return false; // in the future we may support fake TLS handshakes - } - - if ((pl)&&(p >= pl)) { - Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> data(_inbuf + 5,pl - 5); - memmove(_inbuf,_inbuf + pl,p -= pl); - try { - handler(self,arg,_remote,data); - } catch ( ... ) {} // handlers should not throw - pl = 0; - } - } - _inptr = p; - - return true; - } - - inline bool notifyAvailableForWrite(const SharedPtr<Socket> &self,NativeSocketManager *sm) - { - Mutex::Lock _l(_writeLock); - - if (_connecting) - _connecting = false; - - if (_outptr) { - int n = (int)::send(_sock,(const char *)_outbuf,_outptr,0); -#ifdef __WINDOWS__ - if (n == SOCKET_ERROR) { - switch(WSAGetLastError()) { - case WSAEINTR: - case WSAEWOULDBLOCK: - break; - default: - return false; - } -#else - if (n <= 0) { - switch(errno) { -#ifdef EAGAIN - case EAGAIN: -#endif -#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) ) - case EWOULDBLOCK: -#endif -#ifdef EINTR - case EINTR: -#endif - break; - default: - return false; - } -#endif - } else memmove(_outbuf,_outbuf + (unsigned int)n,_outptr -= (unsigned int)n); - } - - if (!_outptr) - sm->_stopNotifyWrite(this); - - return true; - } - - unsigned char _inbuf[ZT_SOCKET_MAX_MESSAGE_LEN]; - unsigned char _outbuf[ZT_SOCKET_MAX_MESSAGE_LEN * 4]; - uint64_t _lastActivity; // updated whenever data is received, checked directly by SocketManager for stale TCP cleanup - NativeSocketManager *_sm; - unsigned int _inptr; - unsigned int _outptr; - bool _connecting; // manipulated directly by SocketManager, true if connect() is in progress - InetAddress _remote; - Mutex _writeLock; -}; - -////////////////////////////////////////////////////////////////////////////// - -#ifdef __WINDOWS__ -// hack copied from StackOverflow, behaves a bit like pipe() on *nix systems -static inline void winPipeHack(SOCKET fds[2]) -{ - struct sockaddr_in inaddr; - struct sockaddr addr; - SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); - memset(&inaddr, 0, sizeof(inaddr)); - memset(&addr, 0, sizeof(addr)); - inaddr.sin_family = AF_INET; - inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - inaddr.sin_port = 0; - int yes=1; - setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); - bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); - listen(lst,1); - int len=sizeof(inaddr); - getsockname(lst, &addr,&len); - fds[0]=::socket(AF_INET, SOCK_STREAM,0); - connect(fds[0],&addr,len); - fds[1]=accept(lst,0,0); - closesocket(lst); -} -#endif - -NativeSocketManager::NativeSocketManager(int localUdpPort,int localTcpPort) : - SocketManager(), - _whackSendPipe(INVALID_SOCKET), - _whackReceivePipe(INVALID_SOCKET), - _tcpV4ListenSocket(INVALID_SOCKET), - _tcpV6ListenSocket(INVALID_SOCKET), - _nfds(0) -{ - FD_ZERO(&_readfds); - FD_ZERO(&_writefds); - - // Create a pipe or socket pair that can be used to interrupt select() -#ifdef __WINDOWS__ - { - SOCKET tmps[2] = { INVALID_SOCKET,INVALID_SOCKET }; - winPipeHack(tmps); - _whackSendPipe = tmps[0]; - _whackReceivePipe = tmps[1]; - u_long iMode=1; - ioctlsocket(tmps[1],FIONBIO,&iMode); - } -#else - { - int tmpfds[2]; - if (::pipe(tmpfds)) - throw std::runtime_error("pipe() failed"); - _whackSendPipe = tmpfds[1]; - _whackReceivePipe = tmpfds[0]; - fcntl(_whackReceivePipe,F_SETFL,O_NONBLOCK); - } -#endif - FD_SET(_whackReceivePipe,&_readfds); - - if (localTcpPort > 0) { - if (localTcpPort > 0xffff) { - _closeSockets(); - throw std::runtime_error("invalid local TCP port number"); - } - - { // bind TCP IPv6 - _tcpV6ListenSocket = ::socket(AF_INET6,SOCK_STREAM,0); -#ifdef __WINDOWS__ - if (_tcpV6ListenSocket != INVALID_SOCKET) { - { - BOOL f; - f = TRUE; ::setsockopt(_tcpV6ListenSocket,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = TRUE; ::setsockopt(_tcpV6ListenSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - u_long iMode=1; - ioctlsocket(_tcpV6ListenSocket,FIONBIO,&iMode); - } -#else - if (_tcpV6ListenSocket > 0) { - { - int f; - f = 1; ::setsockopt(_tcpV6ListenSocket,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); - f = 1; ::setsockopt(_tcpV6ListenSocket,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - fcntl(_tcpV6ListenSocket,F_SETFL,O_NONBLOCK); - } -#endif // __WINDOWS__ / not __WINDOWS__ - - struct sockaddr_in6 sin6; - memset(&sin6,0,sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(localTcpPort); - memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr)); - if (::bind(_tcpV6ListenSocket,(const struct sockaddr *)&sin6,sizeof(sin6))) { - _closeSockets(); - throw std::runtime_error("unable to bind to local TCP port"); - } - - if (::listen(_tcpV6ListenSocket,1024)) { - _closeSockets(); - throw std::runtime_error("listen() failed"); - } - - FD_SET(_tcpV6ListenSocket,&_readfds); - } - } - - { // bind TCP IPv4 - _tcpV4ListenSocket = ::socket(AF_INET,SOCK_STREAM,0); -#ifdef __WINDOWS__ - if (_tcpV4ListenSocket == INVALID_SOCKET) { -#else - if (_tcpV4ListenSocket <= 0) { -#endif - _closeSockets(); - throw std::runtime_error("unable to create IPv4 SOCK_STREAM socket"); - } - -#ifdef __WINDOWS__ - { - BOOL f = TRUE; ::setsockopt(_tcpV4ListenSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - u_long iMode=1; - ioctlsocket(_tcpV4ListenSocket,FIONBIO,&iMode); - } -#else - { - int f = 1; ::setsockopt(_tcpV4ListenSocket,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - fcntl(_tcpV4ListenSocket,F_SETFL,O_NONBLOCK); - } -#endif - - struct sockaddr_in sin4; - memset(&sin4,0,sizeof(sin4)); - sin4.sin_family = AF_INET; - sin4.sin_port = htons(localTcpPort); - sin4.sin_addr.s_addr = INADDR_ANY; - if (::bind(_tcpV4ListenSocket,(const struct sockaddr *)&sin4,sizeof(sin4))) { - _closeSockets(); - throw std::runtime_error("unable to bind to local TCP port"); - } - - if (::listen(_tcpV4ListenSocket,1024)) { - _closeSockets(); - throw std::runtime_error("listen() failed"); - } - - FD_SET(_tcpV4ListenSocket,&_readfds); - } - } - - if (localUdpPort > 0) { - if (localUdpPort > 0xffff) { - _closeSockets(); - throw std::runtime_error("invalid local UDP port number"); - } - - { // bind UDP IPv6 -#ifdef __WINDOWS__ - SOCKET s = ::socket(AF_INET6,SOCK_DGRAM,0); - if (s != INVALID_SOCKET) { -#else - int s = ::socket(AF_INET6,SOCK_DGRAM,0); - if (s > 0) { -#endif - - { - int bs = 1048576; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } - bs = 1048576; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } -#ifdef __WINDOWS__ - BOOL f; - f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); - f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); -#else - int f; - f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); - f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); -#ifdef IP_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); -#endif -#ifdef IP_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); -#endif -#ifdef IPV6_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); -#endif -#endif - } - - struct sockaddr_in6 sin6; - memset(&sin6,0,sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(localUdpPort); - memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr)); - if (::bind(s,(const struct sockaddr *)&sin6,sizeof(sin6))) { - CLOSE_SOCKET(s); - _closeSockets(); - throw std::runtime_error("unable to bind to port"); - } - - _udpV6Socket = SharedPtr<Socket>(new NativeUdpSocket(Socket::ZT_SOCKET_TYPE_UDP_V6,s)); -#ifdef __WINDOWS__ - u_long iMode=1; - ioctlsocket(s,FIONBIO,&iMode); -#else - fcntl(s,F_SETFL,O_NONBLOCK); -#endif - FD_SET(s,&_readfds); - } - } - - { // bind UDP IPv4 -#ifdef __WINDOWS__ - SOCKET s = ::socket(AF_INET,SOCK_DGRAM,0); - if (s == INVALID_SOCKET) { - _closeSockets(); - throw std::runtime_error("unable to create IPv4 SOCK_DGRAM socket"); - } -#else - int s = ::socket(AF_INET,SOCK_DGRAM,0); - if (s <= 0) { - _closeSockets(); - throw std::runtime_error("unable to create IPv4 SOCK_DGRAM socket"); - } -#endif - - { - int bs = 1048576; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } - bs = 1048576; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } -#ifdef __WINDOWS__ - BOOL f; - f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = FALSE; setsockopt(s,IPPROTO_IP,IP_DONTFRAGMENT,(const char *)&f,sizeof(f)); - f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); -#else - int f; - f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); -#ifdef IP_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); -#endif -#ifdef IP_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); -#endif -#endif - } - - struct sockaddr_in sin4; - memset(&sin4,0,sizeof(sin4)); - sin4.sin_family = AF_INET; - sin4.sin_port = htons(localUdpPort); - sin4.sin_addr.s_addr = INADDR_ANY; - if (::bind(s,(const struct sockaddr *)&sin4,sizeof(sin4))) { - CLOSE_SOCKET(s); - _closeSockets(); - throw std::runtime_error("unable to bind to port"); - } - - _udpV4Socket = SharedPtr<Socket>(new NativeUdpSocket(Socket::ZT_SOCKET_TYPE_UDP_V4,s)); -#ifdef __WINDOWS__ - u_long iMode=1; - ioctlsocket(s,FIONBIO,&iMode); -#else - fcntl(s,F_SETFL,O_NONBLOCK); -#endif - FD_SET(s,&_readfds); - } - } - - _updateNfds(); -} - -NativeSocketManager::~NativeSocketManager() -{ - Mutex::Lock _l(_pollLock); - _closeSockets(); -} - -bool NativeSocketManager::send(const InetAddress &to,bool tcp,bool autoConnectTcp,const void *msg,unsigned int msglen) -{ - if (tcp) { - SharedPtr<Socket> ts; - { - Mutex::Lock _l(_tcpSockets_m); - std::map< InetAddress,SharedPtr<Socket> >::iterator opents(_tcpSockets.find(to)); - if (opents != _tcpSockets.end()) - ts = opents->second; - } - if (ts) - return ts->send(to,msg,msglen); - - if (!autoConnectTcp) - return false; - -#ifdef __WINDOWS__ - SOCKET s = ::socket(to.isV4() ? AF_INET : AF_INET6,SOCK_STREAM,0); - if (s == INVALID_SOCKET) - return false; - { u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); } -#ifdef ZT_TCP_NODELAY - { BOOL f = TRUE; setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } -#endif -#else - int s = ::socket(to.isV4() ? AF_INET : AF_INET6,SOCK_STREAM,0); - if (s <= 0) - return false; - if (s >= FD_SETSIZE) { - ::close(s); - return false; - } - fcntl(s,F_SETFL,O_NONBLOCK); -#ifdef ZT_TCP_NODELAY - { int f = 1; setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } -#endif -#endif - - bool connecting = false; - if (::connect(s,to.saddr(),to.saddrLen())) { -#ifdef __WINDOWS__ - if (WSAGetLastError() != WSAEWOULDBLOCK) { -#else - if (errno != EINPROGRESS) { -#endif - CLOSE_SOCKET(s); - return false; - } else connecting = true; - } - - ts = SharedPtr<Socket>(new NativeTcpSocket(this,s,Socket::ZT_SOCKET_TYPE_TCP_OUT,connecting,to)); - if (!ts->send(to,msg,msglen)) { - _fdSetLock.lock(); - FD_CLR(s,&_readfds); - FD_CLR(s,&_writefds); - _fdSetLock.unlock(); - return false; - } - - { - Mutex::Lock _l(_tcpSockets_m); - _tcpSockets[to] = ts; - } - - _fdSetLock.lock(); - FD_SET(s,&_readfds); - if (connecting) - FD_SET(s,&_writefds); - _fdSetLock.unlock(); - - _updateNfds(); - whack(); - - return true; - } else if (to.isV4()) { - if (_udpV4Socket) - return _udpV4Socket->send(to,msg,msglen); - } else if (to.isV6()) { - if (_udpV6Socket) - return _udpV6Socket->send(to,msg,msglen); - } - return false; -} - -void NativeSocketManager::poll(unsigned long timeout,void (*handler)(const SharedPtr<Socket> &,void *,const InetAddress &,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &),void *arg) -{ - fd_set rfds,wfds,efds; - struct timeval tv; - std::vector< SharedPtr<Socket> > ts; -#ifdef __WINDOWS__ - SOCKET sockfd; -#else - int sockfd; -#endif - - Mutex::Lock _l(_pollLock); - - _fdSetLock.lock(); - memcpy(&rfds,&_readfds,sizeof(rfds)); - memcpy(&wfds,&_writefds,sizeof(wfds)); - _fdSetLock.unlock(); - FD_ZERO(&efds); - -#ifdef __WINDOWS__ - // Windows signals failed connects in exceptfds - { - Mutex::Lock _l2(_tcpSockets_m); - for(std::map< InetAddress,SharedPtr<Socket> >::iterator s(_tcpSockets.begin());s!=_tcpSockets.end();++s) { - if (((NativeTcpSocket *)s->second.ptr())->_connecting) - FD_SET(((NativeTcpSocket *)s->second.ptr())->_sock,&efds); - } - } -#endif - - tv.tv_sec = (long)(timeout / 1000); - tv.tv_usec = (long)((timeout % 1000) * 1000); - select(_nfds + 1,&rfds,&wfds,&efds,(timeout > 0) ? &tv : (struct timeval *)0); - - if (FD_ISSET(_whackReceivePipe,&rfds)) { - char tmp[16]; -#ifdef __WINDOWS__ - ::recv(_whackReceivePipe,tmp,16,0); -#else - ::read(_whackReceivePipe,tmp,16); -#endif - } - - if ((_tcpV4ListenSocket != INVALID_SOCKET)&&(FD_ISSET(_tcpV4ListenSocket,&rfds))) { - struct sockaddr_in from; - socklen_t fromlen = sizeof(from); - sockfd = accept(_tcpV4ListenSocket,(struct sockaddr *)&from,&fromlen); -#ifdef __WINDOWS__ - if (sockfd != INVALID_SOCKET) { -#else - if (sockfd > 0) { - if (sockfd < FD_SETSIZE) { -#endif - InetAddress fromia((const struct sockaddr *)&from); - Mutex::Lock _l2(_tcpSockets_m); - try { - _tcpSockets[fromia] = SharedPtr<Socket>(new NativeTcpSocket(this,sockfd,Socket::ZT_SOCKET_TYPE_TCP_IN,false,fromia)); -#ifdef __WINDOWS__ - { u_long iMode=1; ioctlsocket(sockfd,FIONBIO,&iMode); } -#ifdef ZT_TCP_NODELAY - { BOOL f = TRUE; setsockopt(sockfd,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } -#endif -#else - fcntl(sockfd,F_SETFL,O_NONBLOCK); -#ifdef ZT_TCP_NODELAY - { int f = 1; setsockopt(sockfd,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } -#endif -#endif - _fdSetLock.lock(); - FD_SET(sockfd,&_readfds); - _fdSetLock.unlock(); - if ((int)sockfd > (int)_nfds) - _nfds = (int)sockfd; - } catch ( ... ) { - CLOSE_SOCKET(sockfd); - } -#ifndef __WINDOWS__ - } else { - CLOSE_SOCKET(sockfd); - } -#endif - } - } - if ((_tcpV6ListenSocket != INVALID_SOCKET)&&(FD_ISSET(_tcpV6ListenSocket,&rfds))) { - struct sockaddr_in6 from; - socklen_t fromlen = sizeof(from); - sockfd = accept(_tcpV6ListenSocket,(struct sockaddr *)&from,&fromlen); -#ifdef __WINDOWS__ - if (sockfd != INVALID_SOCKET) { -#else - if (sockfd > 0) { - if (sockfd < FD_SETSIZE) { -#endif - InetAddress fromia((const struct sockaddr *)&from); - Mutex::Lock _l2(_tcpSockets_m); - try { - _tcpSockets[fromia] = SharedPtr<Socket>(new NativeTcpSocket(this,sockfd,Socket::ZT_SOCKET_TYPE_TCP_IN,false,fromia)); -#ifdef __WINDOWS__ - { u_long iMode=1; ioctlsocket(sockfd,FIONBIO,&iMode); } -#ifdef ZT_TCP_NODELAY - { BOOL f = TRUE; setsockopt(sockfd,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } -#endif -#else - fcntl(sockfd,F_SETFL,O_NONBLOCK); -#ifdef ZT_TCP_NODELAY - { int f = 1; setsockopt(sockfd,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } -#endif -#endif - _fdSetLock.lock(); - FD_SET(sockfd,&_readfds); - _fdSetLock.unlock(); - if ((int)sockfd > (int)_nfds) - _nfds = (int)sockfd; - } catch ( ... ) { - CLOSE_SOCKET(sockfd); - } -#ifndef __WINDOWS__ - } else { - CLOSE_SOCKET(sockfd); - } -#endif - } - } - - { - NativeUdpSocket *usock = (NativeUdpSocket *)_udpV4Socket.ptr(); - if ((usock)&&(FD_ISSET(usock->_sock,&rfds))) { - usock->notifyAvailableForRead(_udpV4Socket,this,handler,arg); - } - usock = (NativeUdpSocket *)_udpV6Socket.ptr(); - if ((usock)&&(FD_ISSET(usock->_sock,&rfds))) { - usock->notifyAvailableForRead(_udpV6Socket,this,handler,arg); - } - } - - bool closedSockets = false; - { // grab copy of TCP sockets list because _tcpSockets[] might be changed in a handler - Mutex::Lock _l2(_tcpSockets_m); - if (!_tcpSockets.empty()) { - ts.reserve(_tcpSockets.size()); - uint64_t now = Utils::now(); - for(std::map< InetAddress,SharedPtr<Socket> >::iterator s(_tcpSockets.begin());s!=_tcpSockets.end();) { - NativeTcpSocket *tsock = (NativeTcpSocket *)s->second.ptr(); -#ifdef __WINDOWS__ - if ( ((now - tsock->_lastActivity) < ZT_TCP_TUNNEL_ACTIVITY_TIMEOUT) && (! ((tsock->_connecting)&&(FD_ISSET(tsock->_sock,&efds))) ) ) { -#else - if ((now - tsock->_lastActivity) < ZT_TCP_TUNNEL_ACTIVITY_TIMEOUT) { -#endif - ts.push_back(s->second); - ++s; - } else { - _fdSetLock.lock(); - FD_CLR(tsock->_sock,&_readfds); - FD_CLR(tsock->_sock,&_writefds); - _fdSetLock.unlock(); - _tcpSockets.erase(s++); - closedSockets = true; - } - } - } - } - for(std::vector< SharedPtr<Socket> >::iterator s(ts.begin());s!=ts.end();++s) { - NativeTcpSocket *tsock = (NativeTcpSocket *)s->ptr(); - if (FD_ISSET(tsock->_sock,&wfds)) { - if (!tsock->notifyAvailableForWrite(*s,this)) { - { - Mutex::Lock _l2(_tcpSockets_m); - _tcpSockets.erase(tsock->_remote); - } - _fdSetLock.lock(); - FD_CLR(tsock->_sock,&_readfds); - FD_CLR(tsock->_sock,&_writefds); - _fdSetLock.unlock(); - closedSockets = true; - continue; - } - } - if (FD_ISSET(tsock->_sock,&rfds)) { - if (!tsock->notifyAvailableForRead(*s,this,handler,arg)) { - { - Mutex::Lock _l2(_tcpSockets_m); - _tcpSockets.erase(tsock->_remote); - } - _fdSetLock.lock(); - FD_CLR(tsock->_sock,&_readfds); - FD_CLR(tsock->_sock,&_writefds); - _fdSetLock.unlock(); - closedSockets = true; - continue; - } - } - } - if (closedSockets) - _updateNfds(); -} - -void NativeSocketManager::whack() -{ - _whackSendPipe_m.lock(); -#ifdef __WINDOWS__ - ::send(_whackSendPipe,(const char *)this,1,0); -#else - ::write(_whackSendPipe,(const void *)this,1); // data is arbitrary, just send a byte -#endif - _whackSendPipe_m.unlock(); -} - -void NativeSocketManager::closeTcpSockets() -{ - { - Mutex::Lock _l2(_tcpSockets_m); - _fdSetLock.lock(); - for(std::map< InetAddress,SharedPtr<Socket> >::iterator s(_tcpSockets.begin());s!=_tcpSockets.end();++s) { - FD_CLR(((NativeTcpSocket *)s->second.ptr())->_sock,&_readfds); - FD_CLR(((NativeTcpSocket *)s->second.ptr())->_sock,&_writefds); - } - _fdSetLock.unlock(); - _tcpSockets.clear(); - } - _updateNfds(); -} - -void NativeSocketManager::_startNotifyWrite(const NativeSocket *sock) -{ - _fdSetLock.lock(); - FD_SET(sock->_sock,&_writefds); - _fdSetLock.unlock(); -} - -void NativeSocketManager::_stopNotifyWrite(const NativeSocket *sock) -{ - _fdSetLock.lock(); - FD_CLR(sock->_sock,&_writefds); - _fdSetLock.unlock(); -} - -void NativeSocketManager::_closeSockets() -{ -#ifdef __WINDOWS__ - if (_whackSendPipe != INVALID_SOCKET) - ::closesocket(_whackSendPipe); - if (_whackReceivePipe != INVALID_SOCKET) - ::closesocket(_whackReceivePipe); - if (_tcpV4ListenSocket != INVALID_SOCKET) - ::closesocket(_tcpV4ListenSocket); - if (_tcpV6ListenSocket != INVALID_SOCKET) - ::closesocket(_tcpV6ListenSocket); -#else - if (_whackSendPipe > 0) - ::close(_whackSendPipe); - if (_whackReceivePipe > 0) - ::close(_whackReceivePipe); - if (_tcpV4ListenSocket > 0) - ::close(_tcpV4ListenSocket); - if (_tcpV4ListenSocket > 0) - ::close(_tcpV6ListenSocket); -#endif -} - -void NativeSocketManager::_updateNfds() -{ -#ifdef __WINDOWS__ - SOCKET nfds = _whackSendPipe; -#else - int nfds = _whackSendPipe; -#endif - if (_whackReceivePipe > nfds) - nfds = _whackReceivePipe; - if (_tcpV4ListenSocket > nfds) - nfds = _tcpV4ListenSocket; - if (_tcpV6ListenSocket > nfds) - nfds = _tcpV6ListenSocket; - if ((_udpV4Socket)&&(((NativeUdpSocket *)_udpV4Socket.ptr())->_sock > nfds)) - nfds = ((NativeUdpSocket *)_udpV4Socket.ptr())->_sock; - if ((_udpV6Socket)&&(((NativeUdpSocket *)_udpV6Socket.ptr())->_sock > nfds)) - nfds = ((NativeUdpSocket *)_udpV6Socket.ptr())->_sock; - Mutex::Lock _l(_tcpSockets_m); - for(std::map< InetAddress,SharedPtr<Socket> >::const_iterator s(_tcpSockets.begin());s!=_tcpSockets.end();++s) { - if (((NativeTcpSocket *)s->second.ptr())->_sock > nfds) - nfds = ((NativeTcpSocket *)s->second.ptr())->_sock; - } - _nfds = (int)nfds; -} - -} // namespace ZeroTier diff --git a/osnet/NativeSocketManager.hpp b/osnet/NativeSocketManager.hpp deleted file mode 100644 index 5db06d6a..00000000 --- a/osnet/NativeSocketManager.hpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_NATIVESOCKETMANAGER_HPP -#define ZT_NATIVESOCKETMANAGER_HPP - -#include <stdio.h> -#include <stdlib.h> - -#include <map> -#include <stdexcept> - -#include "../node/Constants.hpp" -#include "../node/SharedPtr.hpp" -#include "../node/Mutex.hpp" -#include "../node/SocketManager.hpp" -#include "../node/Socket.hpp" - -#ifdef __WINDOWS__ -#include <WinSock2.h> -#include <WS2tcpip.h> -#include <Windows.h> -#else -#include <unistd.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/select.h> -#endif - -namespace ZeroTier { - -class NativeSocket; -class NativeUdpSocket; -class NativeTcpSocket; - -/** - * Native socket manager for Unix and Windows - */ -class NativeSocketManager : public SocketManager -{ - friend class NativeUdpSocket; - friend class NativeTcpSocket; - -public: - NativeSocketManager(int localUdpPort,int localTcpPort); - virtual ~NativeSocketManager(); - - virtual bool send(const InetAddress &to,bool tcp,bool autoConnectTcp,const void *msg,unsigned int msglen); - virtual void poll(unsigned long timeout,void (*handler)(const SharedPtr<Socket> &,void *,const InetAddress &,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &),void *arg); - virtual void whack(); - virtual void closeTcpSockets(); - -private: - // Used by TcpSocket to register/unregister for write availability notification - void _startNotifyWrite(const NativeSocket *sock); - void _stopNotifyWrite(const NativeSocket *sock); - - // Called in SocketManager destructor or in constructor cleanup before exception throwing - void _closeSockets(); - - // Called in SocketManager to recompute _nfds for select() based implementation - void _updateNfds(); - -#ifdef __WINDOWS__ - SOCKET _whackSendPipe; - SOCKET _whackReceivePipe; - SOCKET _tcpV4ListenSocket; - SOCKET _tcpV6ListenSocket; -#else - int _whackSendPipe; - int _whackReceivePipe; - int _tcpV4ListenSocket; - int _tcpV6ListenSocket; -#endif - Mutex _whackSendPipe_m; - - SharedPtr<Socket> _udpV4Socket; - SharedPtr<Socket> _udpV6Socket; - - fd_set _readfds; - fd_set _writefds; - volatile int _nfds; - Mutex _fdSetLock; - - std::map< InetAddress,SharedPtr<Socket> > _tcpSockets; - Mutex _tcpSockets_m; - - Mutex _pollLock; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/OSXEthernetTap.cpp b/osnet/OSXEthernetTap.cpp deleted file mode 100644 index 396605c5..00000000 --- a/osnet/OSXEthernetTap.cpp +++ /dev/null @@ -1,639 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <unistd.h> -#include <signal.h> - -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/select.h> -#include <sys/cdefs.h> -#include <sys/uio.h> -#include <sys/param.h> -#include <sys/sysctl.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <net/route.h> -#include <net/if.h> -#include <net/if_arp.h> -#include <net/if_dl.h> -#include <net/if_media.h> -#include <netinet6/in6_var.h> -#include <netinet/in_var.h> -#include <netinet/icmp6.h> - -// OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!? -struct prf_ra { - u_char onlink : 1; - u_char autonomous : 1; - u_char reserved : 6; -} prf_ra; - -#include <netinet6/nd6.h> -#include <ifaddrs.h> - -// These are KERNEL_PRIVATE... why? -#ifndef SIOCAUTOCONF_START -#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */ -#endif -#ifndef SIOCAUTOCONF_STOP -#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */ -#endif - -// -------------------------------------------------------------------------- -// -------------------------------------------------------------------------- -// This source is from: -// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt -// It's here because OSX 10.6 does not have this convenience function. - -#define SALIGN (sizeof(uint32_t) - 1) -#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ -(SALIGN + 1)) -#define MAX_SYSCTL_TRY 5 -#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) - -/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */ -/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */ -//#define DARWIN_COMPAT - -//#ifdef DARWIN_COMPAT -#define GIM_SYSCTL_MIB NET_RT_IFLIST2 -#define GIM_RTM_ADDR RTM_NEWMADDR2 -//#else -//#define GIM_SYSCTL_MIB NET_RT_IFMALIST -//#define GIM_RTM_ADDR RTM_NEWMADDR -//#endif - -// Not in 10.6 includes so use our own -struct _intl_ifmaddrs { - struct _intl_ifmaddrs *ifma_next; - struct sockaddr *ifma_name; - struct sockaddr *ifma_addr; - struct sockaddr *ifma_lladdr; -}; - -static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif) -{ - int icnt = 1; - int dcnt = 0; - int ntry = 0; - size_t len; - size_t needed; - int mib[6]; - int i; - char *buf; - char *data; - char *next; - char *p; - struct ifma_msghdr2 *ifmam; - struct _intl_ifmaddrs *ifa, *ift; - struct rt_msghdr *rtm; - struct sockaddr *sa; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; /* protocol */ - mib[3] = 0; /* wildcard address family */ - mib[4] = GIM_SYSCTL_MIB; - mib[5] = 0; /* no flags */ - do { - if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) - return (-1); - if ((buf = (char *)malloc(needed)) == NULL) - return (-1); - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { - if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { - free(buf); - return (-1); - } - free(buf); - buf = NULL; - } - } while (buf == NULL); - - for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)(void *)next; - if (rtm->rtm_version != RTM_VERSION) - continue; - switch (rtm->rtm_type) { - case GIM_RTM_ADDR: - ifmam = (struct ifma_msghdr2 *)(void *)rtm; - if ((ifmam->ifmam_addrs & RTA_IFA) == 0) - break; - icnt++; - p = (char *)(ifmam + 1); - for (i = 0; i < RTAX_MAX; i++) { - if ((RTA_MASKS & ifmam->ifmam_addrs & - (1 << i)) == 0) - continue; - sa = (struct sockaddr *)(void *)p; - len = SA_RLEN(sa); - dcnt += len; - p += len; - } - break; - } - } - - data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt); - if (data == NULL) { - free(buf); - return (-1); - } - - ifa = (struct _intl_ifmaddrs *)(void *)data; - data += sizeof(struct _intl_ifmaddrs) * icnt; - - memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt); - ift = ifa; - - for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)(void *)next; - if (rtm->rtm_version != RTM_VERSION) - continue; - - switch (rtm->rtm_type) { - case GIM_RTM_ADDR: - ifmam = (struct ifma_msghdr2 *)(void *)rtm; - if ((ifmam->ifmam_addrs & RTA_IFA) == 0) - break; - - p = (char *)(ifmam + 1); - for (i = 0; i < RTAX_MAX; i++) { - if ((RTA_MASKS & ifmam->ifmam_addrs & - (1 << i)) == 0) - continue; - sa = (struct sockaddr *)(void *)p; - len = SA_RLEN(sa); - switch (i) { - case RTAX_GATEWAY: - ift->ifma_lladdr = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - case RTAX_IFP: - ift->ifma_name = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - case RTAX_IFA: - ift->ifma_addr = - (struct sockaddr *)(void *)data; - memcpy(data, p, len); - data += len; - break; - - default: - data += len; - break; - } - p += len; - } - ift->ifma_next = ift + 1; - ift = ift->ifma_next; - break; - } - } - - free(buf); - - if (ift > ifa) { - ift--; - ift->ifma_next = NULL; - *pif = ifa; - } else { - *pif = NULL; - free(ifa); - } - return (0); -} - -static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp) -{ - free(ifmp); -} - -// -------------------------------------------------------------------------- -// -------------------------------------------------------------------------- - -#include <string> -#include <map> -#include <set> -#include <algorithm> - -#include "../node/Constants.hpp" -#include "../node/Utils.hpp" -#include "../node/Mutex.hpp" -#include "OSXEthernetTap.hpp" - -// ff:ff:ff:ff:ff:ff with no ADI -static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); - -static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts) -{ - struct in6_ndireq nd; - struct in6_ifreq ifr; - - int s = socket(AF_INET6,SOCK_DGRAM,0); - if (s <= 0) - return false; - - memset(&nd,0,sizeof(nd)); - strncpy(nd.ifname,ifname,sizeof(nd.ifname)); - - if (ioctl(s,SIOCGIFINFO_IN6,&nd)) { - close(s); - return false; - } - - unsigned long oldFlags = (unsigned long)nd.ndi.flags; - - if (performNUD) - nd.ndi.flags |= ND6_IFF_PERFORMNUD; - else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD; - - if (oldFlags != (unsigned long)nd.ndi.flags) { - if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) { - close(s); - return false; - } - } - - memset(&ifr,0,sizeof(ifr)); - strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name)); - if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) { - close(s); - return false; - } - - close(s); - return true; -} - -namespace ZeroTier { - -OSXEthernetTap::OSXEthernetTap( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) : - EthernetTap("OSXEthernetTap",mac,mtu,metric), - _handler(handler), - _arg(arg), - _mtu(mtu), - _metric(metric), - _fd(0), - _enabled(true) -{ - static Mutex globalTapCreateLock; - char devpath[64],ethaddr[64],mtustr[32],metstr[32]; - struct stat stattmp; - - Mutex::Lock _gl(globalTapCreateLock); - - if (mtu > 2800) - throw std::runtime_error("max tap MTU is 2800"); - if (stat("/dev/zt0",&stattmp)) - throw std::runtime_error("/dev/zt# tap devices do not exist"); - - // Try to reopen the last device we had, if we had one and it's still unused. - bool recalledDevice = false; - if ((desiredDevice)&&(desiredDevice[0] == 'z')&&(desiredDevice[1] == 't')) { - if ((strchr(desiredDevice,'/'))||(strchr(desiredDevice,'.'))) // security sanity check - throw std::runtime_error("invalid desiredDevice parameter"); - Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice); - if (stat(devpath,&stattmp) == 0) { - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) { - _dev = desiredDevice; - recalledDevice = true; - } - } - } - - // 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); - 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); - _dev = foo; - break; - } - } - } - - if (_fd <= 0) - throw std::runtime_error("unable to open TAP device or no more devices available"); - - if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { - ::close(_fd); - throw std::runtime_error("unable to set flags on file descriptor for TAP device"); - } - - // 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); - 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); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - if (exitcode) { - ::close(_fd); - throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); - } - } - - _setIpv6Stuff(_dev.c_str(),true,false); - - // Set close-on-exec so that devices cannot persist if we fork/exec for update - fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); - - ::pipe(_shutdownSignalPipe); - - _thread = Thread::start(this); -} - -OSXEthernetTap::~OSXEthernetTap() -{ - ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit - Thread::join(_thread); - ::close(_fd); - ::close(_shutdownSignalPipe[0]); - ::close(_shutdownSignalPipe[1]); -} - -void OSXEthernetTap::setEnabled(bool en) -{ - _enabled = en; - // TODO: interface status change -} - -bool OSXEthernetTap::enabled() const -{ - return _enabled; -} - -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); - _exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; // never reached, make compiler shut up about return value -} - -bool OSXEthernetTap::addIP(const InetAddress &ip) -{ - if (!ip) - return false; - - std::set<InetAddress> allIps(ips()); - if (allIps.count(ip) > 0) - return true; // IP/netmask already assigned - - // Remove and reconfigure if address is the same but netmask is different - for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { - if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { - if (___removeIp(_dev,*i)) - break; - } - } - - 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); - ::_exit(-1); - } else if (cpid > 0) { - int exitcode = -1; - ::waitpid(cpid,&exitcode,0); - return (exitcode == 0); - } - return false; -} - -bool OSXEthernetTap::removeIP(const InetAddress &ip) -{ - if (ips().count(ip) > 0) { - if (___removeIp(_dev,ip)) - return true; - } - return false; -} - -std::set<InetAddress> OSXEthernetTap::ips() const -{ - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (getifaddrs(&ifa)) - return std::set<InetAddress>(); - - std::set<InetAddress> r; - - struct ifaddrs *p = ifa; - while (p) { - if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { - switch(p->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; - struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; - r.insert(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); - } break; - case AF_INET6: { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; - struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; - uint32_t b[4]; - memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); - r.insert(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); - } break; - } - } - p = p->ifa_next; - } - - if (ifa) - freeifaddrs(ifa); - - return r; -} - -void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - char putBuf[4096]; - if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { - to.copyTo(putBuf,6); - from.copyTo(putBuf + 6,6); - *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); - memcpy(putBuf + 14,data,len); - len += 14; - ::write(_fd,putBuf,len); - } -} - -std::string OSXEthernetTap::deviceName() const -{ - return _dev; -} - -void OSXEthernetTap::setFriendlyName(const char *friendlyName) -{ -} - -bool OSXEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) -{ - std::set<MulticastGroup> newGroups; - struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0; - if (!_intl_getifmaddrs(&ifmap)) { - struct _intl_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; - } - _intl_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; -} - -bool OSXEthernetTap::injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - return false; -} - -void OSXEthernetTap::threadMain() - throw() -{ - fd_set readfds,nullfds; - MAC to,from; - int n,nfds,r; - char getBuf[8194]; - Buffer<4096> data; - - // Wait for a moment after startup -- wait for Network to finish - // constructing itself. - Thread::sleep(500); - - FD_ZERO(&readfds); - FD_ZERO(&nullfds); - nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; - - r = 0; - for(;;) { - FD_SET(_shutdownSignalPipe[0],&readfds); - FD_SET(_fd,&readfds); - select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); - - if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread - break; - - if (FD_ISSET(_fd,&readfds)) { - n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); - if (n < 0) { - if ((errno != EINTR)&&(errno != ETIMEDOUT)) - break; - } else { - // Some tap drivers like to send the ethernet frame and the - // payload in two chunks, so handle that by accumulating - // data until we have at least a frame. - r += n; - if (r > 14) { - if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms - r = _mtu + 14; - - if (_enabled) { - to.setTo(getBuf,6); - from.setTo(getBuf + 6,6); - unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); - data.copyFrom(getBuf + 14,(unsigned int)r - 14); - _handler(_arg,from,to,etherType,data); - } - - r = 0; - } - } - } - } -} - -} // namespace ZeroTier diff --git a/osnet/OSXEthernetTap.hpp b/osnet/OSXEthernetTap.hpp deleted file mode 100644 index efcb550f..00000000 --- a/osnet/OSXEthernetTap.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_OSXETHERNETTAP_HPP -#define ZT_OSXETHERNETTAP_HPP - -#include <stdio.h> -#include <stdlib.h> - -#include <stdexcept> - -#include "../node/EthernetTap.hpp" -#include "../node/Thread.hpp" - -namespace ZeroTier { - -/** - * OSX Ethernet tap using ZeroTier kernel extension zt# devices - * - * This also installs a friendly-named network device in the system network - * configuration, permitting network devices to be seen and configured in - * the OSX system preferences app. - */ -class OSXEthernetTap : public EthernetTap -{ -public: - OSXEthernetTap( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - - virtual ~OSXEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIP(const InetAddress &ip); - virtual bool removeIP(const InetAddress &ip); - virtual std::set<InetAddress> ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual bool updateMulticastGroups(std::set<MulticastGroup> &groups); - virtual bool injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - - void threadMain() - throw(); - -private: - void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); - void *_arg; - Thread _thread; - std::string _dev; - unsigned int _mtu; - unsigned int _metric; - int _fd; - int _shutdownSignalPipe[2]; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/OSXEthernetTapFactory.cpp b/osnet/OSXEthernetTapFactory.cpp deleted file mode 100644 index 4cad8daa..00000000 --- a/osnet/OSXEthernetTapFactory.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/stat.h> - -#include "OSXEthernetTapFactory.hpp" -#include "OSXEthernetTap.hpp" - -#include "../node/Utils.hpp" - -namespace ZeroTier { - -OSXEthernetTapFactory::OSXEthernetTapFactory(const char *pathToTapKext,const char *tapKextName) : - _pathToTapKext((pathToTapKext) ? pathToTapKext : ""), - _tapKextName((tapKextName) ? tapKextName : "") -{ - struct stat stattmp; - - if ((_pathToTapKext.length())&&(_tapKextName.length())) { - if (stat("/dev/zt0",&stattmp)) { - long kextpid = (long)vfork(); - if (kextpid == 0) { - ::chdir(_pathToTapKext.c_str()); - Utils::redirectUnixOutputs("/dev/null",(const char *)0); - ::execl("/sbin/kextload","/sbin/kextload","-q","-repository",_pathToTapKext.c_str(),_tapKextName.c_str(),(const char *)0); - ::_exit(-1); - } else if (kextpid > 0) { - int exitcode = -1; - ::waitpid(kextpid,&exitcode,0); - } else throw std::runtime_error("unable to create subprocess with fork()"); - } - } - - if (stat("/dev/zt0",&stattmp)) { - ::usleep(500); // give tap device driver time to start up and try again - if (stat("/dev/zt0",&stattmp)) - throw std::runtime_error("/dev/zt# tap devices do not exist and unable to load kernel extension"); - } -} - -OSXEthernetTapFactory::~OSXEthernetTapFactory() -{ - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) - delete *d; - - if ((_pathToTapKext.length())&&(_tapKextName.length())) { - // Attempt to unload kext. If anything else is using a /dev/zt# node, this - // fails and the kext stays in the kernel. - char tmp[16384]; - sprintf(tmp,"%s/%s",_pathToTapKext.c_str(),_tapKextName.c_str()); - long kextpid = (long)vfork(); - if (kextpid == 0) { - Utils::redirectUnixOutputs("/dev/null",(const char *)0); - ::execl("/sbin/kextunload","/sbin/kextunload",tmp,(const char *)0); - ::_exit(-1); - } else if (kextpid > 0) { - int exitcode = -1; - ::waitpid(kextpid,&exitcode,0); - } - } -} - -EthernetTap *OSXEthernetTapFactory::open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) -{ - Mutex::Lock _l(_devices_m); - EthernetTap *t = new OSXEthernetTap(mac,mtu,metric,nwid,desiredDevice,friendlyName,handler,arg); - _devices.push_back(t); - return t; -} - -void OSXEthernetTapFactory::close(EthernetTap *tap,bool destroyPersistentDevices) -{ - { - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) { - if (*d == tap) { - _devices.erase(d); - break; - } - } - } - delete tap; -} - -} // namespace ZeroTier diff --git a/osnet/OSXEthernetTapFactory.hpp b/osnet/OSXEthernetTapFactory.hpp deleted file mode 100644 index 2f2ee527..00000000 --- a/osnet/OSXEthernetTapFactory.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_OSXETHERNETTAPFACTORY_HPP -#define ZT_OSXETHERNETTAPFACTORY_HPP - -#include <vector> -#include <string> - -#include "../node/EthernetTapFactory.hpp" -#include "../node/Mutex.hpp" - -namespace ZeroTier { - -class OSXEthernetTapFactory : public EthernetTapFactory -{ -public: - /** - * Create OSX ethernet tap factory - * - * If kext paths are specified, an attempt will be made to load the kext - * on launch if not present and unload it on shutdown. - * - * @param pathToTapKext Full path to the location of the tap kext - * @param tapKextName Name of tap kext as found within tap kext path (usually "tap.kext") - * @throws std::runtime_error Tap not available and unable to load kext - */ - OSXEthernetTapFactory(const char *pathToTapKext,const char *tapKextName); - - virtual ~OSXEthernetTapFactory(); - - virtual EthernetTap *open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - virtual void close(EthernetTap *tap,bool destroyPersistentDevices); - -private: - std::vector<EthernetTap *> _devices; - Mutex _devices_m; - std::string _pathToTapKext; - std::string _tapKextName; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/Phy.hpp b/osnet/Phy.hpp deleted file mode 100644 index 6abdf8ad..00000000 --- a/osnet/Phy.hpp +++ /dev/null @@ -1,817 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_PHY_HPP -#define ZT_PHY_HPP - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <list> - -#if defined(_WIN32) || defined(_WIN64) - -#include <WinSock2.h> -#include <WS2tcpip.h> -#include <Windows.h> - -#define ZT_PHY_SOCKFD_TYPE SOCKET -#define ZT_PHY_SOCKFD_NULL (INVALID_SOCKET) -#define ZT_PHY_SOCKFD_VALID(s) ((s) != INVALID_SOCKET) -#define ZT_PHY_CLOSE_SOCKET(s) ::closesocket(s) -#define ZT_PHY_MAX_SOCKETS (FD_SETSIZE) -#define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage - -#else // not Windows - -#include <errno.h> -#include <signal.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/select.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <netinet/tcp.h> - -#define ZT_PHY_SOCKFD_TYPE int -#define ZT_PHY_SOCKFD_NULL (-1) -#define ZT_PHY_SOCKFD_VALID(s) ((s) > -1) -#define ZT_PHY_CLOSE_SOCKET(s) ::close(s) -#define ZT_PHY_MAX_SOCKETS (FD_SETSIZE) -#define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage - -#endif // Windows or not - -namespace ZeroTier { - -/** - * Opaque socket type - */ -typedef void PhySocket; - -/** - * Simple templated non-blocking sockets implementation - * - * Yes there is boost::asio and libuv, but I like small binaries and I hate - * build dependencies. Both drag in a whole bunch of pasta with them. - * - * This implementation takes four functions or function objects as template - * paramters: - * - * ON_DATAGRAM_FUNCTION(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) - * ON_TCP_CONNECT_FUNCTION(PhySocket *sock,void **uptr,bool success) - * ON_TCP_ACCEPT_FUNCTION(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) - * ON_TCP_CLOSE_FUNCTION(PhySocket *sock,void **uptr) - * ON_TCP_DATA_FUNCTION(PhySocket *sock,void **uptr,void *data,unsigned long len) - * ON_TCP_WRITABLE_FUNCTION(PhySocket *sock,void **uptr) - * - * These templates typically refer to function objects. Templates are used to - * avoid the call overhead of indirection, which is surprisingly high for high - * bandwidth applications pushing a lot of packets. - * - * The 'sock' pointer above is an opaque pointer to a socket. Each socket - * has a 'uptr' user-settable/modifiable pointer associated with it, which - * can be set on bind/connect calls and is passed as a void ** to permit - * resetting at any time. The ACCEPT handler takes two sets of sock and - * uptr: sockL and uptrL for the listen socket, and sockN and uptrN for - * the new TCP connection socket that has just been created. - * - * Handlers are always called. On outgoing TCP connection, CONNECT is always - * called on either success or failure followed by DATA and/or WRITABLE as - * indicated. On socket close, handlers are called unless close() is told - * explicitly not to call handlers. It is safe to close a socket within a - * handler, and in that case close() can be told not to call handlers to - * prevent recursion. - * - * This isn't thread-safe with the exception of whack(), which is safe to - * call from another thread to abort poll(). - */ -template < - typename ON_DATAGRAM_FUNCTION, - typename ON_TCP_CONNECT_FUNCTION, - typename ON_TCP_ACCEPT_FUNCTION, - typename ON_TCP_CLOSE_FUNCTION, - typename ON_TCP_DATA_FUNCTION, - typename ON_TCP_WRITABLE_FUNCTION > -class Phy -{ -private: - ON_DATAGRAM_FUNCTION _datagramHandler; - ON_TCP_CONNECT_FUNCTION _tcpConnectHandler; - ON_TCP_ACCEPT_FUNCTION _tcpAcceptHandler; - ON_TCP_CLOSE_FUNCTION _tcpCloseHandler; - ON_TCP_DATA_FUNCTION _tcpDataHandler; - ON_TCP_WRITABLE_FUNCTION _tcpWritableHandler; - - enum PhySocketType - { - ZT_PHY_SOCKET_TCP_OUT_PENDING = 0x00, - ZT_PHY_SOCKET_TCP_OUT_CONNECTED = 0x01, - ZT_PHY_SOCKET_TCP_IN = 0x02, - ZT_PHY_SOCKET_TCP_LISTEN = 0x03, - ZT_PHY_SOCKET_RAW = 0x04, - ZT_PHY_SOCKET_UDP = 0x05 - }; - - struct PhySocketImpl - { - PhySocketType type; - ZT_PHY_SOCKFD_TYPE sock; - void *uptr; // user-settable pointer - ZT_PHY_SOCKADDR_STORAGE_TYPE saddr; // remote for TCP_OUT and TCP_IN, local for TCP_LISTEN, RAW, and UDP - }; - - std::list<PhySocketImpl> _socks; - fd_set _readfds; - fd_set _writefds; -#if defined(_WIN32) || defined(_WIN64) - fd_set _exceptfds; -#endif - long _nfds; - - ZT_PHY_SOCKFD_TYPE _whackReceiveSocket; - ZT_PHY_SOCKFD_TYPE _whackSendSocket; - - bool _noDelay; - -public: - /** - * @param datagramHandler Function or function object to handle UDP or RAW datagrams - * @param tcpConnectHandler Handler for outgoing TCP connection attempts (success or failure) - * @param tcpAcceptHandler Handler for incoming TCP connections - * @param tcpDataHandler Handler for incoming TCP data - * @param tcpWritableHandler Handler to be called when TCP sockets are writable (if notification is on) - * @param noDelay If true, disable Nagle algorithm on new TCP sockets - */ - Phy( - ON_DATAGRAM_FUNCTION datagramHandler, - ON_TCP_CONNECT_FUNCTION tcpConnectHandler, - ON_TCP_ACCEPT_FUNCTION tcpAcceptHandler, - ON_TCP_CLOSE_FUNCTION tcpCloseHandler, - ON_TCP_DATA_FUNCTION tcpDataHandler, - ON_TCP_WRITABLE_FUNCTION tcpWritableHandler, - bool noDelay - ) : - _datagramHandler(datagramHandler), - _tcpConnectHandler(tcpConnectHandler), - _tcpAcceptHandler(tcpAcceptHandler), - _tcpCloseHandler(tcpCloseHandler), - _tcpDataHandler(tcpDataHandler), - _tcpWritableHandler(tcpWritableHandler) - { - FD_ZERO(&_readfds); - FD_ZERO(&_writefds); - -#if defined(_WIN32) || defined(_WIN64) - FD_ZERO(&_exceptfds); - - SOCKET pipes[2]; - { // hack copied from StackOverflow, behaves a bit like pipe() on *nix systems - struct sockaddr_in inaddr; - struct sockaddr addr; - SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); - if (lst == INVALID_SOCKET) - throw std::runtime_error("unable to create pipes for select() abort"); - memset(&inaddr, 0, sizeof(inaddr)); - memset(&addr, 0, sizeof(addr)); - inaddr.sin_family = AF_INET; - inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - inaddr.sin_port = 0; - int yes=1; - setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); - bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); - listen(lst,1); - int len=sizeof(inaddr); - getsockname(lst, &addr,&len); - pipes[0]=::socket(AF_INET, SOCK_STREAM,0); - if (pipes[0] == INVALID_SOCKET) - throw std::runtime_error("unable to create pipes for select() abort"); - connect(pipes[0],&addr,len); - pipes[1]=accept(lst,0,0); - closesocket(lst); - } -#else // not Windows - int pipes[2]; - if (::pipe(pipes)) - throw std::runtime_error("unable to create pipes for select() abort"); -#endif // Windows or not - - _nfds = (pipes[0] > pipes[1]) ? (long)pipes[0] : (long)pipes[1]; - _whackReceiveSocket = pipes[0]; - _whackSendSocket = pipes[1]; - _noDelay = noDelay; - } - - ~Phy() - { - while (!_socks.empty()) - this->close((PhySocket *)&(_socks.front()),true); - ZT_PHY_CLOSE_SOCKET(_whackReceiveSocket); - ZT_PHY_CLOSE_SOCKET(_whackSendSocket); - } - - /** - * Cause poll() to stop waiting immediately - */ - inline void whack() - { -#if defined(_WIN32) || defined(_WIN64) - ::send(_whackSendSocket,(const char *)this,1,0); -#else - ::write(_whackSendSocket,(PhySocket *)this,1); -#endif - } - - /** - * @return Number of open sockets - */ - inline unsigned long count() const throw() { return _socks.size(); } - - /** - * @return Maximum number of sockets allowed - */ - inline unsigned long maxCount() const throw() { return ZT_PHY_MAX_SOCKETS; } - - /** - * Bind a UDP socket - * - * @param localAddress Local endpoint address and port - * @param uptr Initial value of user pointer associated with this socket (default: NULL) - * @param bufferSize Desired socket receive/send buffer size -- will set as close to this as possible (default: 0, leave alone) - * @return Socket or NULL on failure to bind - */ - inline PhySocket *udpBind(const struct sockaddr *localAddress,void *uptr = (void *)0,int bufferSize = 0) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - ZT_PHY_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_DGRAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) - return (PhySocket *)0; - - if (bufferSize > 0) { - int bs = bufferSize; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } - bs = bufferSize; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } - } - -#if defined(_WIN32) || defined(_WIN64) - { - BOOL f; - if (localAddress->sa_family == AF_INET6) { - f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); - } - f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); - } -#else // not Windows - { - int f; - if (localAddress->sa_family == AF_INET6) { - f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); -#ifdef IPV6_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); -#endif - } - f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); -#ifdef IP_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); -#endif -#ifdef IP_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); -#endif - } -#endif // Windows or not - - if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - -#if defined(_WIN32) || defined(_WIN64) - { u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); } -#else - fcntl(s,F_SETFL,O_NONBLOCK); -#endif - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_UDP; - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - - return (PhySocket *)&sws; - } - - /** - * Send a UDP packet - * - * @param sock UDP socket - * @param remoteAddress Destination address (must be correct type for socket) - * @param data Data to send - * @param len Length of packet - * @return True if packet appears to have been sent successfully - */ - inline bool udpSend(PhySocket *sock,const struct sockaddr *remoteAddress,const void *data,unsigned long len) - { - PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); - return ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len); - } - - /** - * Bind a local listen socket to listen for new TCP connections - * - * @param localAddress Local address and port - * @param uptr Initial value of uptr for new socket (default: NULL) - * @return Socket or NULL on failure to bind - */ - inline PhySocket *tcpListen(const struct sockaddr *localAddress,void *uptr = (void *)0) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - ZT_PHY_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_STREAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) - return (PhySocket *)0; - -#if defined(_WIN32) || defined(_WIN64) - { - BOOL f; - f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - u_long iMode=1; - ioctlsocket(s,FIONBIO,&iMode); - } -#else - { - int f; - f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); - f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - fcntl(s,F_SETFL,O_NONBLOCK); - } -#endif - - if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - - if (::listen(s,1024)) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_TCP_LISTEN; - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - - return (PhySocket *)&sws; - } - - /** - * Start a non-blocking connect; CONNECT handler is called on success or failure - * - * A return value of NULL indicates a synchronous failure such as a - * failure to open a socket. The TCP connection handler is not called - * in this case. - * - * It is possible on some platforms for an "instant connect" to occur, - * such as when connecting to a loopback address. In this case, the - * 'connected' result parameter will be set to 'true' and if the - * 'callConnectHandler' flag is true (the default) the TCP connect - * handler will be called before the function returns. - * - * These semantics can be a bit confusing, but they're less so than - * the underlying semantics of asynchronous TCP connect. - * - * @param remoteAddress Remote address - * @param connected Result parameter: set to whether an "instant connect" has occurred (true if yes) - * @param uptr Initial value of uptr for new socket (default: NULL) - * @param callConnectHandler If true, call TCP connect handler even if result is known before function exit (default: true) - * @return New socket or NULL on failure - */ - inline PhySocket *tcpConnect(const struct sockaddr *remoteAddress,bool &connected,void *uptr = (void *)0,bool callConnectHandler = true) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - ZT_PHY_SOCKFD_TYPE s = ::socket(remoteAddress->sa_family,SOCK_STREAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) { - connected = false; - return (PhySocket *)0; - } - -#if defined(_WIN32) || defined(_WIN64) - { - BOOL f; - f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - u_long iMode=1; - ioctlsocket(s,FIONBIO,&iMode); - } -#else - { - int f; - f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); - f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - fcntl(s,F_SETFL,O_NONBLOCK); - } -#endif - - connected = true; - if (::connect(s,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - connected = false; -#if defined(_WIN32) || defined(_WIN64) - if (WSAGetLastError() != WSAEWOULDBLOCK) { -#else - if (errno != EINPROGRESS) { -#endif - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } // else connection is proceeding asynchronously... - } - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - if (connected) { - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_TCP_OUT_CONNECTED; - } else { - FD_SET(s,&_writefds); -#if defined(_WIN32) || defined(_WIN64) - FD_SET(s,&_exceptfds); -#endif - sws.type = ZT_PHY_SOCKET_TCP_OUT_PENDING; - } - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - - if ((callConnectHandler)&&(connected)) { - try { - _tcpConnectHandler((PhySocket *)&sws,&(sws.uptr),true); - } catch ( ... ) {} - } - - return (PhySocket *)&sws; - } - - /** - * Attempt to send data to a TCP connection (non-blocking) - * - * If -1 is returned, the socket should no longer be used as it is now - * destroyed. If callCloseHandler is true, the close handler will be - * called before the function returns. - * - * @param sock An open TCP socket (other socket types will fail) - * @param data Data to send - * @param len Length of data - * @param callCloseHandler If true, call close handler on socket closing failure condition - * @return Number of bytes actually sent or -1 on fatal error (socket closure) - */ - inline long tcpSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler) - { - PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); - long n = (long)::send(sws.sock,data,len,0); -#if defined(_WIN32) || defined(_WIN64) - if (n == SOCKET_ERROR) { - switch(WSAGetLastError()) { - case WSAEINTR: - case WSAEWOULDBLOCK: - return 0; - default: - this->close(sock,callCloseHandler); - return -1; - } - } -#else // not Windows - if (n < 0) { - switch(errno) { -#ifdef EAGAIN - case EAGAIN: -#endif -#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) ) - case EWOULDBLOCK: -#endif -#ifdef EINTR - case EINTR: -#endif - return 0; - default: - this->close(sock,callCloseHandler); - return -1; - } - } -#endif // Windows or not - return n; - } - - /** - * Set whether we want to be notified via the TCP writability handler when a socket is writable - * - * Call whack() if this is being done from another thread and you want - * it to take effect immediately. Otherwise it is only guaranteed to - * take effect on the next poll(). - * - * @param sock TCP connection socket (other types are not valid) - * @param notifyWritable Want writable notifications? - */ - inline const void tcpSetNotifyWritable(PhySocket *sock,bool notifyWritable) - { - PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); - if (notifyWritable) { - FD_SET(sws.sock,&_writefds); - } else { - FD_CLR(sws.sock,&_writefds); - } - } - - /** - * Wait for activity and handle one or more events - * - * Note that this is not guaranteed to wait up to 'timeout' even - * if nothing happens, as whack() or other events such as signals - * may cause premature termination. - * - * @param timeout Timeout in milliseconds or 0 for none (forever) - */ - inline void poll(unsigned long timeout) - { - char buf[131072]; - struct sockaddr_storage ss; - struct timeval tv; - fd_set rfds,wfds,efds; - - memcpy(&rfds,&_readfds,sizeof(rfds)); - memcpy(&wfds,&_writefds,sizeof(wfds)); -#if defined(_WIN32) || defined(_WIN64) - memcpy(&efds,&_exceptfds,sizeof(efds)); -#else - FD_ZERO(&efds); -#endif - - tv.tv_sec = (long)(timeout / 1000); - tv.tv_usec = (long)((timeout % 1000) * 1000); - if (::select((int)_nfds + 1,&rfds,&wfds,&efds,(timeout > 0) ? &tv : (struct timeval *)0) <= 0) - return; - - if (FD_ISSET(_whackReceiveSocket,&rfds)) { - char tmp[16]; -#if defined(_WIN32) || defined(_WIN64) - ::recv(_whackReceiveSocket,tmp,16,0); -#else - ::read(_whackReceiveSocket,tmp,16); -#endif - } - - for(typename std::list<PhySocketImpl>::iterator s(_socks.begin()),nexts;s!=_socks.end();s=nexts) { - nexts = s; ++nexts; // we can delete the linked list item, so traverse now - - switch (s->type) { - - case ZT_PHY_SOCKET_TCP_OUT_PENDING: -#if defined(_WIN32) || defined(_WIN64) - if (FD_ISSET(s->sock,&efds)) - this->close((PhySocket *)&(*s),true); - else // ... if -#endif - if (FD_ISSET(s->sock,&wfds)) { - socklen_t slen = sizeof(ss); - if (::getpeername(s->sock,(struct sockaddr *)&ss,&slen) != 0) { - this->close((PhySocket *)&(*s),true); - } else { - s->type = ZT_PHY_SOCKET_TCP_OUT_CONNECTED; - FD_SET(s->sock,&_readfds); - FD_CLR(s->sock,&_writefds); -#if defined(_WIN32) || defined(_WIN64) - FD_CLR(s->sock,&_exceptfds); -#endif - try { - _tcpConnectHandler((PhySocket *)&(*s),&(s->uptr),true); - } catch ( ... ) {} - } - } - break; - - case ZT_PHY_SOCKET_TCP_OUT_CONNECTED: - case ZT_PHY_SOCKET_TCP_IN: - if (FD_ISSET(s->sock,&rfds)) { - long n = (long)::recv(s->sock,buf,sizeof(buf),0); - if (n <= 0) { - this->close((PhySocket *)&(*s),true); - } else { - try { - _tcpDataHandler((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n); - } catch ( ... ) {} - } - } - if ((FD_ISSET(s->sock,&wfds))&&(FD_ISSET(s->sock,&_writefds))) { - try { - _tcpWritableHandler((PhySocket *)&(*s),&(s->uptr)); - } catch ( ... ) {} - } - break; - - case ZT_PHY_SOCKET_TCP_LISTEN: - if (FD_ISSET(s->sock,&rfds)) { - memset(&ss,0,sizeof(ss)); - socklen_t slen = sizeof(ss); - ZT_PHY_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen); - if (ZT_PHY_SOCKFD_VALID(newSock)) { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) { - ZT_PHY_CLOSE_SOCKET(newSock); - } else { -#if defined(_WIN32) || defined(_WIN64) - { BOOL f = (_noDelay ? TRUE : FALSE); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } - { u_long iMode=1; ioctlsocket(newSock,FIONBIO,&iMode); } -#else - { int f = (_noDelay ? 1 : 0); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } - fcntl(newSock,F_SETFL,O_NONBLOCK); -#endif - _socks.push_back(PhySocketImpl()); - PhySocketImpl &sws = _socks.back(); - FD_SET(newSock,&_readfds); - if ((long)newSock > _nfds) - _nfds = (long)newSock; - sws.type = ZT_PHY_SOCKET_TCP_IN; - sws.sock = newSock; - sws.uptr = (void *)0; - memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage)); - try { - _tcpAcceptHandler((PhySocket *)&(*s),(PhySocket *)&(_socks.back()),&(s->uptr),&(sws.uptr),(const struct sockaddr *)&(sws.saddr)); - } catch ( ... ) {} - } - } - } - break; - - case ZT_PHY_SOCKET_UDP: - if (FD_ISSET(s->sock,&rfds)) { - memset(&ss,0,sizeof(ss)); - socklen_t slen = sizeof(ss); - long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); - if (n > 0) { - try { - _datagramHandler((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); - } catch ( ... ) {} - } - } - break; - - default: - break; - - } - } - } - - inline void close(PhySocket *sock,bool callHandlers) - { - if (!sock) - return; - PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); - - FD_CLR(sws.sock,&_readfds); - FD_CLR(sws.sock,&_writefds); -#if defined(_WIN32) || defined(_WIN64) - FD_CLR(sws.sock,&_exceptfds); -#endif - - ZT_PHY_CLOSE_SOCKET(sws.sock); - - switch(sws.type) { - case ZT_PHY_SOCKET_TCP_OUT_PENDING: - if (callHandlers) { - try { - _tcpConnectHandler(sock,&(sws.uptr),false); - } catch ( ... ) {} - } - break; - case ZT_PHY_SOCKET_TCP_OUT_CONNECTED: - case ZT_PHY_SOCKET_TCP_IN: - if (callHandlers) { - try { - _tcpCloseHandler(sock,&(sws.uptr)); - } catch ( ... ) {} - } - break; - default: - break; - } - - long oldSock = (long)sws.sock; - - for(typename std::list<PhySocketImpl>::iterator s(_socks.begin());s!=_socks.end();++s) { - if (reinterpret_cast<PhySocket *>(&(*s)) == sock) { - _socks.erase(s); - break; - } - } - - if (oldSock >= _nfds) { - long nfds = (long)_whackSendSocket; - if ((long)_whackReceiveSocket > nfds) - nfds = (long)_whackReceiveSocket; - for(typename std::list<PhySocketImpl>::iterator s(_socks.begin());s!=_socks.end();++s) { - if ((long)s->sock > nfds) - nfds = (long)s->sock; - } - _nfds = nfds; - } - } -}; - -// Typedefs for using regular naked functions as template parameters to Phy<> -typedef void (*Phy_OnDatagramFunctionPtr)(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len); -typedef void (*Phy_OnTcpConnectFunction)(PhySocket *sock,void **uptr,bool success); -typedef void (*Phy_OnTcpAcceptFunction)(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from); -typedef void (*Phy_OnTcpCloseFunction)(PhySocket *sock,void **uptr); -typedef void (*Phy_OnTcpDataFunction)(PhySocket *sock,void **uptr,void *data,unsigned long len); -typedef void (*Phy_OnTcpWritableFunction)(PhySocket *sock,void **uptr); - -/** - * Phy<> typedef'd to use simple naked function pointers - */ -typedef Phy<Phy_OnDatagramFunctionPtr,Phy_OnTcpConnectFunction,Phy_OnTcpAcceptFunction,Phy_OnTcpCloseFunction,Phy_OnTcpDataFunction,Phy_OnTcpWritableFunction> SimpleFunctionPhy; - -} // namespace ZeroTier - -#endif diff --git a/osnet/README.md b/osnet/README.md deleted file mode 100644 index 114e26f2..00000000 --- a/osnet/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Network and Virtual Network Port Interfaces for Real OSes -====== - -This folder contains implementations of EthernetTap, EthernetTapFactory, and RoutingTable that bind to operating system level interfaces and drivers on Linux, Mac, Windows, and other platforms. - -It also contains NativeSocketManager which implements SocketManager using standard sockets (or WinSock2) and select() for multiplexing. diff --git a/osnet/WindowsEthernetTap.cpp b/osnet/WindowsEthernetTap.cpp deleted file mode 100644 index 5d445380..00000000 --- a/osnet/WindowsEthernetTap.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <WinSock2.h> -#include <Windows.h> -#include <tchar.h> -#include <winreg.h> -#include <wchar.h> -#include <ws2ipdef.h> -#include <WS2tcpip.h> -#include <IPHlpApi.h> -#include <nldef.h> -#include <netioapi.h> -#include <atlbase.h> -#include <netlistmgr.h> -#include <nldef.h> - -#include <iostream> - -#include "../node/Constants.hpp" - -#include "WindowsEthernetTap.hpp" -#include "WindowsEthernetTapFactory.hpp" -#include "../node/Utils.hpp" -#include "../node/Mutex.hpp" - -#include "..\windows\TapDriver\tap-windows.h" - -// ff:ff:ff:ff:ff:ff with no ADI -static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); - -#define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE - -namespace ZeroTier { - -// Only create or delete devices one at a time -static Mutex _systemTapInitLock; - -WindowsEthernetTap::WindowsEthernetTap( - const char *pathToHelpers, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) : - EthernetTap("WindowsEthernetTap",mac,mtu,metric), - _handler(handler), - _arg(arg), - _nwid(nwid), - _tap(INVALID_HANDLE_VALUE), - _injectSemaphore(INVALID_HANDLE_VALUE), - _pathToHelpers(pathToHelpers), - _run(true), - _initialized(false), - _enabled(true) -{ - char subkeyName[4096]; - char subkeyClass[4096]; - char data[4096]; - char tag[24]; - - if (mtu > 2800) - throw std::runtime_error("MTU too large for Windows tap"); - - Mutex::Lock _l(_systemTapInitLock); - - HKEY nwAdapters; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS) - throw std::runtime_error("unable to open registry key for network adapter enumeration"); - - std::set<std::string> existingDeviceInstances; - std::string mySubkeyName; - - // We "tag" registry entries with the network ID to identify persistent devices - Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid); - - // Look for the tap instance that corresponds with this network - for(DWORD subkeyIndex=0;;++subkeyIndex) { - DWORD type; - DWORD dataLen; - DWORD subkeyNameLen = sizeof(subkeyName); - DWORD subkeyClassLen = sizeof(subkeyClass); - FILETIME lastWriteTime; - if (RegEnumKeyExA(nwAdapters,subkeyIndex,subkeyName,&subkeyNameLen,(DWORD *)0,subkeyClass,&subkeyClassLen,&lastWriteTime) == ERROR_SUCCESS) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - data[dataLen] = '\0'; - if (!strnicmp(data,"zttap",5)) { - std::string instanceId; - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - instanceId.assign(data,dataLen); - existingDeviceInstances.insert(instanceId); - } - - std::string instanceIdPath; - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) - instanceIdPath.assign(data,dataLen); - - if ((_netCfgInstanceId.length() == 0)&&(instanceId.length() != 0)&&(instanceIdPath.length() != 0)) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - data[dataLen] = '\0'; - if (!strcmp(data,tag)) { - _netCfgInstanceId = instanceId; - _deviceInstanceId = instanceIdPath; - - mySubkeyName = subkeyName; - break; // found it! - } - } - } - } - } - } else break; // no more subkeys or error occurred enumerating them - } - - // If there is no device, try to create one - bool creatingNewDevice = (_netCfgInstanceId.length() == 0); - if (creatingNewDevice) { - // Log devcon output to a file - HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - if (devconLog != INVALID_HANDLE_VALUE) - SetFilePointer(devconLog,0,0,FILE_END); - - // Execute devcon to install an instance of the Microsoft Loopback Adapter - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - SetFilePointer(devconLog,0,0,FILE_END); - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WindowsEthernetTapFactory::WINENV.devcon + "\" install \"" + _pathToHelpers + WindowsEthernetTapFactory::WINENV.tapDriver + "\" zttap200").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - RegCloseKey(nwAdapters); - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - throw std::runtime_error(std::string("unable to find or execute devcon at ") + WindowsEthernetTapFactory::WINENV.devcon); - } - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - - // Scan for the new instance by simply looking for taps that weren't originally there... - for(DWORD subkeyIndex=0;;++subkeyIndex) { - DWORD type; - DWORD dataLen; - DWORD subkeyNameLen = sizeof(subkeyName); - DWORD subkeyClassLen = sizeof(subkeyClass); - FILETIME lastWriteTime; - if (RegEnumKeyExA(nwAdapters,subkeyIndex,subkeyName,&subkeyNameLen,(DWORD *)0,subkeyClass,&subkeyClassLen,&lastWriteTime) == ERROR_SUCCESS) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - data[dataLen] = '\0'; - if (!strnicmp(data,"zttap",5)) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - if (existingDeviceInstances.count(std::string(data,dataLen)) == 0) { - RegSetKeyValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",REG_SZ,tag,(DWORD)(strlen(tag)+1)); - _netCfgInstanceId.assign(data,dataLen); - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) - _deviceInstanceId.assign(data,dataLen); - mySubkeyName = subkeyName; - - // Disable DHCP by default on newly created devices - HKEY tcpIpInterfaces; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) { - DWORD enable = 0; - RegSetKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable)); - RegCloseKey(tcpIpInterfaces); - } - - break; // found it! - } - } - } - } - } else break; // no more keys or error occurred - } - } - - 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); - DWORD tmp = mtu; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); - - tmp = 0; - 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)); - - if (creatingNewDevice) { - tmp = 0; - RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); - } - RegCloseKey(nwAdapters); - } else { - RegCloseKey(nwAdapters); - throw std::runtime_error("unable to find or create tap adapter"); - } - - // Convert device GUID junk... blech... is there an easier way to do this? - { - char nobraces[128]; - const char *nbtmp1 = _netCfgInstanceId.c_str(); - char *nbtmp2 = nobraces; - while (*nbtmp1) { - if ((*nbtmp1 != '{')&&(*nbtmp1 != '}')) - *nbtmp2++ = *nbtmp1; - ++nbtmp1; - } - *nbtmp2 = (char)0; - if (UuidFromStringA((RPC_CSTR)nobraces,&_deviceGuid) != RPC_S_OK) - throw std::runtime_error("unable to convert instance ID GUID to native GUID (invalid NetCfgInstanceId in registry?)"); - } - - // Look up interface LUID... why are there (at least) four fucking ways to refer to a network device in Windows? - if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR) - throw std::runtime_error("unable to convert device interface GUID to LUID"); - - if (friendlyName) - setFriendlyName(friendlyName); - - // Start background thread that actually performs I/O - _injectSemaphore = CreateSemaphore(NULL,0,1,NULL); - _thread = Thread::start(this); - - // Certain functions can now work (e.g. ips()) - _initialized = true; -} - -WindowsEthernetTap::~WindowsEthernetTap() -{ - _run = false; - ReleaseSemaphore(_injectSemaphore,1,NULL); - Thread::join(_thread); - CloseHandle(_injectSemaphore); - _disableTapDevice(); -} - -void WindowsEthernetTap::setEnabled(bool en) -{ - _enabled = en; -} - -bool WindowsEthernetTap::enabled() const -{ - return _enabled; -} - -bool WindowsEthernetTap::addIP(const InetAddress &ip) -{ - if (!_initialized) - return false; - if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT? - return false; - - std::set<InetAddress> haveIps(ips()); - - try { - // Add IP to interface at the netlink level if not already assigned. - if (!haveIps.count(ip)) { - MIB_UNICASTIPADDRESS_ROW ipr; - - InitializeUnicastIpAddressEntry(&ipr); - if (ip.isV4()) { - ipr.Address.Ipv4.sin_family = AF_INET; - ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)ip.rawIpData()); - ipr.OnLinkPrefixLength = ip.port(); - if (ipr.OnLinkPrefixLength >= 32) - return false; - } else if (ip.isV6()) { - ipr.Address.Ipv6.sin6_family = AF_INET6; - memcpy(ipr.Address.Ipv6.sin6_addr.u.Byte,ip.rawIpData(),16); - ipr.OnLinkPrefixLength = ip.port(); - if (ipr.OnLinkPrefixLength >= 128) - return false; - } else return false; - - ipr.PrefixOrigin = IpPrefixOriginManual; - ipr.SuffixOrigin = IpSuffixOriginManual; - ipr.ValidLifetime = 0xffffffff; - ipr.PreferredLifetime = 0xffffffff; - - ipr.InterfaceLuid = _deviceLuid; - ipr.InterfaceIndex = _getDeviceIndex(); - - if (CreateUnicastIpAddressEntry(&ipr) == NO_ERROR) { - haveIps.insert(ip); - } else { - return false; - } - } - - std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); - if (std::find(regIps.begin(),regIps.end(),ip.toIpString()) == regIps.end()) { - std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask")); - regIps.push_back(ip.toIpString()); - regSubnetMasks.push_back(ip.netmask().toIpString()); - _setRegistryIPv4Value("IPAddress",regIps); - _setRegistryIPv4Value("SubnetMask",regSubnetMasks); - } - //_syncIpsWithRegistry(haveIps,_netCfgInstanceId); - } catch ( ... ) { - return false; - } - return true; -} - -bool WindowsEthernetTap::removeIP(const InetAddress &ip) -{ - if (!_initialized) - return false; - try { - MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; - if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { - for(DWORD i=0;i<ipt->NumEntries;++i) { - if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - InetAddress addr; - switch(ipt->Table[i].Address.si_family) { - case AF_INET: - addr.set(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength); - break; - case AF_INET6: - addr.set(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); - if (addr.isLinkLocal()) - continue; // can't remove link-local IPv6 addresses - break; - } - if (addr == ip) { - DeleteUnicastIpAddressEntry(&(ipt->Table[i])); - FreeMibTable(ipt); - - 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; - } - } - - return true; - } - } - } - FreeMibTable((PVOID)ipt); - } - } catch ( ... ) {} - return false; -} - -std::set<InetAddress> WindowsEthernetTap::ips() const -{ - static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it? - std::set<InetAddress> addrs; - - if (!_initialized) - return addrs; - - try { - MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0; - if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) { - for(DWORD i=0;i<ipt->NumEntries;++i) { - if (ipt->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - switch(ipt->Table[i].Address.si_family) { - case AF_INET: { - InetAddress ip(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength); - if (ip != InetAddress::LO4) - addrs.insert(ip); - } break; - case AF_INET6: { - InetAddress ip(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); - if ((ip != linkLocalLoopback)&&(ip != InetAddress::LO6)) - addrs.insert(ip); - } break; - } - } - } - FreeMibTable(ipt); - } - } catch ( ... ) {} // sanity check, shouldn't happen unless out of memory - - return addrs; -} - -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))) - 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; - to.copyTo(d,6); - from.copyTo(d + 6,6); - d[12] = (char)((etherType >> 8) & 0xff); - d[13] = (char)(etherType & 0xff); - memcpy(d + 14,data,len); - - ReleaseSemaphore(_injectSemaphore,1,NULL); -} - -std::string WindowsEthernetTap::deviceName() const -{ - char tmp[1024]; - if (ConvertInterfaceLuidToNameA(&_deviceLuid,tmp,sizeof(tmp)) != NO_ERROR) - return std::string("[ConvertInterfaceLuidToName() failed]"); - return std::string(tmp); -} - -void WindowsEthernetTap::setFriendlyName(const char *dn) -{ - if (!_initialized) - return; - HKEY ifp; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _netCfgInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) { - RegSetKeyValueA(ifp,"Connection","Name",REG_SZ,(LPCVOID)dn,(DWORD)(strlen(dn)+1)); - RegCloseKey(ifp); - } -} - -bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) -{ - if (!_initialized) - return false; - HANDLE t = _tap; - if (t == INVALID_HANDLE_VALUE) - return false; - - std::set<MulticastGroup> newGroups; - - // Ensure that groups are added for each IP... this handles the MAC:ADI - // groups that are created from IPv4 addresses. Some of these may end - // up being duplicates of what the IOCTL returns but that's okay since - // the set<> will filter that. - std::set<InetAddress> ipaddrs(ips()); - for(std::set<InetAddress>::const_iterator i(ipaddrs.begin());i!=ipaddrs.end();++i) - newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i)); - - // The ZT1 tap driver supports an IOCTL to get multicast memberships at the L2 - // level... something Windows does not seem to expose ordinarily. This lets - // 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)) { - MAC mac; - DWORD i = 0; - while ((i + 6) <= bytesReturned) { - mac.setTo(mcastbuf + i,6); - i += 6; - if ((mac.isMulticast())&&(!mac.isBroadcast())) { - // exclude the nulls that may be returned or any other junk Windows puts in there - newGroups.insert(MulticastGroup(mac,0)); - } - } - } - - 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; -} - -bool WindowsEthernetTap::injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) -{ - return false; -} - -void WindowsEthernetTap::threadMain() - throw() -{ - char tapPath[256]; - OVERLAPPED tapOvlRead,tapOvlWrite; - HANDLE wait4[3]; - char *tapReadBuf = (char *)0; - - // Shouldn't be needed, but Windows does not overcommit. This Windows - // tap code is defensive to schizoid paranoia degrees. - while (!tapReadBuf) { - tapReadBuf = (char *)::malloc(ZT_IF_MTU + 32); - if (!tapReadBuf) - Sleep(1000); - } - - // Tap is in this weird Windows global pseudo file space - Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); - - /* More insanity: repetatively try to enable/disable tap device. The first - * time we succeed, close it and do it again. This is to fix a driver init - * bug that seems to be extremely non-deterministic and to only occur after - * headless MSI upgrade. It cannot be reproduced in any other circumstance. - * - * Eventually when ZeroTier has actual money we will have someone create an - * NDIS6 tap driver. Yes, we'll likely be cool and open source it. */ - bool throwOneAway = true; - while (_run) { - _disableTapDevice(); - Sleep(250); - if (!_enableTapDevice()) { - ::free(tapReadBuf); - _enabled = false; - return; // only happens if devcon is missing or totally fails - } - Sleep(250); - - _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); - if (_tap == INVALID_HANDLE_VALUE) { - Sleep(500); - continue; - } - - { - uint32_t tmpi = 1; - DWORD bytesReturned = 0; - DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); - bytesReturned = 0; - DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); - } - - { -#ifdef ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE - /* This inserts a fake default route and a fake ARP entry, forcing - * Windows to detect this as a "real" network and apply proper - * firewall rules. - * - * This hack is completely stupid, but Windows made me do it - * by being broken and insane. - * - * Background: Windows tries to detect its network location by - * matching it to the ARP address of the default route. Networks - * without default routes are "unidentified networks" and cannot - * have their firewall classification changed by the user (easily). - * - * Yes, you read that right. - * - * The common workaround is to set *NdisDeviceType to 1, which - * totally disables all Windows firewall functionality. This is - * the answer you'll find on most forums for things like OpenVPN. - * - * Yes, you read that right. - * - * The default route workaround is also known, but for this to - * work there must be a known default IP that resolves to a known - * ARP address. This works for an OpenVPN tunnel, but not here - * because this isn't a tunnel. It's a mesh. There is no "other - * end," or any other known always on IP. - * - * So let's make a fake one and shove it in there along with its - * fake static ARP entry. Also makes it instant-on and static. - * - * We'll have to see what DHCP does with this. In the future we - * probably will not want to do this on DHCP-enabled networks, so - * when we enable DHCP we will go in and yank this wacko hacko from - * the routing table before doing so. - * - * Like Jesse Pinkman would say: "YEEEEAAH BITCH!" */ - const uint32_t fakeIp = htonl(0x19fffffe); // 25.255.255.254 -- unrouted IPv4 block - for(int i=0;i<8;++i) { - MIB_IPNET_ROW2 ipnr; - memset(&ipnr,0,sizeof(ipnr)); - ipnr.Address.si_family = AF_INET; - ipnr.Address.Ipv4.sin_addr.s_addr = fakeIp; - ipnr.InterfaceLuid.Value = _deviceLuid.Value; - ipnr.PhysicalAddress[0] = _mac[0] ^ 0x10; // just make something up that's consistent and not part of this net - ipnr.PhysicalAddress[1] = 0x00; - ipnr.PhysicalAddress[2] = (UCHAR)((_deviceGuid.Data1 >> 24) & 0xff); - ipnr.PhysicalAddress[3] = (UCHAR)((_deviceGuid.Data1 >> 16) & 0xff); - ipnr.PhysicalAddress[4] = (UCHAR)((_deviceGuid.Data1 >> 8) & 0xff); - ipnr.PhysicalAddress[5] = (UCHAR)(_deviceGuid.Data1 & 0xff); - ipnr.PhysicalAddressLength = 6; - ipnr.State = NlnsPermanent; - ipnr.IsRouter = 1; - ipnr.IsUnreachable = 0; - ipnr.ReachabilityTime.LastReachable = 0x0fffffff; - ipnr.ReachabilityTime.LastUnreachable = 1; - DWORD result = CreateIpNetEntry2(&ipnr); - if (result != NO_ERROR) - Sleep(500); - else break; - } - for(int i=0;i<8;++i) { - MIB_IPFORWARD_ROW2 nr; - memset(&nr,0,sizeof(nr)); - InitializeIpForwardEntry(&nr); - nr.InterfaceLuid.Value = _deviceLuid.Value; - nr.DestinationPrefix.Prefix.si_family = AF_INET; // rest is left as 0.0.0.0/0 - nr.NextHop.si_family = AF_INET; - nr.NextHop.Ipv4.sin_addr.s_addr = fakeIp; - nr.Metric = 9999; // do not use as real default route - nr.Protocol = MIB_IPPROTO_NETMGMT; - DWORD result = CreateIpForwardEntry2(&nr); - if (result != NO_ERROR) - Sleep(500); - else break; - } -#endif - } - - if (throwOneAway) { - throwOneAway = false; - CloseHandle(_tap); - _tap = INVALID_HANDLE_VALUE; - Sleep(1000); - continue; - } else break; - } - - memset(&tapOvlRead,0,sizeof(tapOvlRead)); - tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); - memset(&tapOvlWrite,0,sizeof(tapOvlWrite)); - tapOvlWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); - - wait4[0] = _injectSemaphore; - wait4[1] = tapOvlRead.hEvent; - wait4[2] = tapOvlWrite.hEvent; // only included if writeInProgress is true - - // Start overlapped read, which is always active - ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead); - bool writeInProgress = false; - - for(;;) { - if (!_run) break; - DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,5000,TRUE); - if (!_run) break; - - if ((r == WAIT_TIMEOUT)||(r == WAIT_FAILED)) - continue; - - if (HasOverlappedIoCompleted(&tapOvlRead)) { - DWORD bytesRead = 0; - if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) { - if ((bytesRead > 14)&&(_enabled)) { - MAC to(tapReadBuf,6); - MAC from(tapReadBuf + 6,6); - unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff); - try { - Buffer<4096> tmp(tapReadBuf + 14,bytesRead - 14); - _handler(_arg,from,to,etherType,tmp); - } catch ( ... ) {} // handlers should not throw - } - } - ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead); - } - - if (writeInProgress) { - if (HasOverlappedIoCompleted(&tapOvlWrite)) { - writeInProgress = false; - _injectPending_m.lock(); - _injectPending.pop(); - } else continue; // still writing, so skip code below and wait - } else _injectPending_m.lock(); - - if (!_injectPending.empty()) { - WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite); - writeInProgress = true; - } - - _injectPending_m.unlock(); - } - - CancelIo(_tap); - - CloseHandle(tapOvlRead.hEvent); - CloseHandle(tapOvlWrite.hEvent); - CloseHandle(_tap); - _tap = INVALID_HANDLE_VALUE; - - ::free(tapReadBuf); -} - -bool WindowsEthernetTap::_disableTapDevice() -{ - HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - if (devconLog != INVALID_HANDLE_VALUE) - SetFilePointer(devconLog,0,0,FILE_END); - - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WindowsEthernetTapFactory::WINENV.devcon + "\" disable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - return false; - } - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - - return true; -} - -bool WindowsEthernetTap::_enableTapDevice() -{ - HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - if (devconLog != INVALID_HANDLE_VALUE) - SetFilePointer(devconLog,0,0,FILE_END); - - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WindowsEthernetTapFactory::WINENV.devcon + "\" enable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - return false; - } - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); - - return true; -} - -NET_IFINDEX WindowsEthernetTap::_getDeviceIndex() -{ - MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0; - - if (GetIfTable2Ex(MibIfTableRaw,&ift) != NO_ERROR) - throw std::runtime_error("GetIfTable2Ex() failed"); - - for(ULONG i=0;i<ift->NumEntries;++i) { - if (ift->Table[i].InterfaceLuid.Value == _deviceLuid.Value) { - NET_IFINDEX idx = ift->Table[i].InterfaceIndex; - FreeMibTable(ift); - return idx; - } - } - - FreeMibTable(&ift); - - throw std::runtime_error("interface not found"); -} - -std::vector<std::string> WindowsEthernetTap::_getRegistryIPv4Value(const char *regKey) -{ - std::vector<std::string> value; - HKEY tcpIpInterfaces; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) { - char buf[16384]; - DWORD len = sizeof(buf); - DWORD kt = REG_MULTI_SZ; - if (RegGetValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),regKey,0,&kt,&buf,&len) == ERROR_SUCCESS) { - switch(kt) { - case REG_SZ: - if (len > 0) - value.push_back(std::string(buf)); - break; - case REG_MULTI_SZ: { - for(DWORD k=0,s=0;k<len;++k) { - if (!buf[k]) { - if (s < k) { - value.push_back(std::string(buf + s)); - s = k + 1; - } else break; - } - } - } break; - } - } - RegCloseKey(tcpIpInterfaces); - } - return value; -} - -void WindowsEthernetTap::_setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value) -{ - std::string regMulti; - for(std::vector<std::string>::const_iterator s(value.begin());s!=value.end();++s) { - regMulti.append(*s); - regMulti.push_back((char)0); - } - HKEY tcpIpInterfaces; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) { - if (regMulti.length() > 0) { - regMulti.push_back((char)0); - RegSetKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),regKey,REG_MULTI_SZ,regMulti.data(),(DWORD)regMulti.length()); - } else { - RegDeleteKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),regKey); - } - RegCloseKey(tcpIpInterfaces); - } -} - -} // namespace ZeroTier diff --git a/osnet/WindowsEthernetTap.hpp b/osnet/WindowsEthernetTap.hpp deleted file mode 100644 index 08dc8d17..00000000 --- a/osnet/WindowsEthernetTap.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_WINDOWSETHERNETTAP_HPP -#define ZT_WINDOWSETHERNETTAP_HPP - -#include <stdio.h> -#include <stdlib.h> - -#include <ifdef.h> - -#include <string> -#include <queue> -#include <stdexcept> - -#include "../node/Constants.hpp" -#include "../node/EthernetTap.hpp" -#include "../node/Mutex.hpp" -#include "../node/Thread.hpp" -#include "../node/Array.hpp" -#include "../node/MulticastGroup.hpp" - -namespace ZeroTier { - -class WindowsEthernetTap : public EthernetTap -{ -public: - WindowsEthernetTap( - const char *pathToHelpers, - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - - virtual ~WindowsEthernetTap(); - - virtual void setEnabled(bool en); - virtual bool enabled() const; - virtual bool addIP(const InetAddress &ip); - virtual bool removeIP(const InetAddress &ip); - virtual std::set<InetAddress> ips() const; - virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - virtual std::string deviceName() const; - virtual void setFriendlyName(const char *friendlyName); - virtual bool updateMulticastGroups(std::set<MulticastGroup> &groups); - virtual bool injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); - - inline const NET_LUID &luid() const { return _deviceLuid; } - inline const GUID &guid() const { return _deviceGuid; } - inline const std::string &instanceId() const { return _deviceInstanceId; } - - void threadMain() - throw(); - -private: - bool _disableTapDevice(); - bool _enableTapDevice(); - NET_IFINDEX _getDeviceIndex(); // throws on failure - std::vector<std::string> _getRegistryIPv4Value(const char *regKey); - void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value); - - void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); - void *_arg; - uint64_t _nwid; - Thread _thread; - - volatile HANDLE _tap; - HANDLE _injectSemaphore; - - GUID _deviceGuid; - NET_LUID _deviceLuid; - std::string _netCfgInstanceId; // NetCfgInstanceId, a GUID - std::string _deviceInstanceId; // DeviceInstanceID, another kind of "instance ID" - - std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending; - Mutex _injectPending_m; - - std::string _pathToHelpers; - - volatile bool _run; - volatile bool _initialized; - volatile bool _enabled; -}; - -} // namespace ZeroTier - -#endif diff --git a/osnet/WindowsEthernetTapFactory.cpp b/osnet/WindowsEthernetTapFactory.cpp deleted file mode 100644 index 996460a1..00000000 --- a/osnet/WindowsEthernetTapFactory.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include "WindowsEthernetTapFactory.hpp" -#include "WindowsEthernetTap.hpp" - -namespace ZeroTier { - -WindowsEthernetTapFactory::Env::Env() -{ -#ifdef _WIN64 - is64Bit = TRUE; - devcon = "\\devcon_x64.exe"; - tapDriver = "\\tap-windows\\x64\\zttap200.inf"; -#else - is64Bit = FALSE; - IsWow64Process(GetCurrentProcess(),&is64Bit); - devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe"); - tapDriver = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap200.inf" : "\\tap-windows\\x86\\zttap200.inf"); -#endif -} -const WindowsEthernetTapFactory::Env WindowsEthernetTapFactory::WINENV; - -WindowsEthernetTapFactory::WindowsEthernetTapFactory(const char *pathToHelpers) : - _pathToHelpers(pathToHelpers) -{ -} - -WindowsEthernetTapFactory::~WindowsEthernetTapFactory() -{ - Mutex::Lock _l(_devices_m); - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) - delete *d; -} - -EthernetTap *WindowsEthernetTapFactory::open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg) -{ - Mutex::Lock _l(_devices_m); - EthernetTap *t = new WindowsEthernetTap(_pathToHelpers.c_str(),mac,mtu,metric,nwid,desiredDevice,friendlyName,handler,arg); - _devices.push_back(t); - return t; -} - -void WindowsEthernetTapFactory::close(EthernetTap *tap,bool destroyPersistentDevices) -{ - if (!tap) - return; - - std::string instanceId(((WindowsEthernetTap *)tap)->instanceId()); - Mutex::Lock _l(_devices_m); - - for(std::vector<EthernetTap *>::iterator d(_devices.begin());d!=_devices.end();++d) { - if (*d == tap) { - _devices.erase(d); - break; - } - } - - delete tap; - - if (destroyPersistentDevices) - _deletePersistentTapDevice(_pathToHelpers.c_str(),instanceId.c_str()); -} - -void WindowsEthernetTapFactory::destroyAllPersistentTapDevices(const char *pathToHelpers) -{ - char subkeyName[4096]; - char subkeyClass[4096]; - char data[4096]; - - std::set<std::string> instanceIdPathsToRemove; - { - HKEY nwAdapters; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS) - return; - - for(DWORD subkeyIndex=0;;++subkeyIndex) { - DWORD type; - DWORD dataLen; - DWORD subkeyNameLen = sizeof(subkeyName); - DWORD subkeyClassLen = sizeof(subkeyClass); - FILETIME lastWriteTime; - if (RegEnumKeyExA(nwAdapters,subkeyIndex,subkeyName,&subkeyNameLen,(DWORD *)0,subkeyClass,&subkeyClassLen,&lastWriteTime) == ERROR_SUCCESS) { - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { - data[dataLen] = '\0'; - if (!strnicmp(data,"zttap",5)) { - std::string instanceIdPath; - type = 0; - dataLen = sizeof(data); - if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) - instanceIdPath.assign(data,dataLen); - if (instanceIdPath.length() != 0) - instanceIdPathsToRemove.insert(instanceIdPath); - } - } - } else break; // end of list or failure - } - - RegCloseKey(nwAdapters); - } - - for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) - _deletePersistentTapDevice(pathToHelpers,iidp->c_str()); -} - -void WindowsEthernetTapFactory::_deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId) -{ - HANDLE devconLog = CreateFileA((std::string(pathToHelpers) + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - STARTUPINFOA startupInfo; - startupInfo.cb = sizeof(startupInfo); - if (devconLog != INVALID_HANDLE_VALUE) { - SetFilePointer(devconLog,0,0,FILE_END); - startupInfo.hStdOutput = devconLog; - startupInfo.hStdError = devconLog; - } - PROCESS_INFORMATION processInfo; - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + pathToHelpers + WINENV.devcon + "\" remove @" + instanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - if (devconLog != INVALID_HANDLE_VALUE) - CloseHandle(devconLog); -} - -} // namespace ZeroTier diff --git a/osnet/WindowsEthernetTapFactory.hpp b/osnet/WindowsEthernetTapFactory.hpp deleted file mode 100644 index 47e146e3..00000000 --- a/osnet/WindowsEthernetTapFactory.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_WINDOWSETHERNETTAPFACTORY_HPP -#define ZT_WINDOWSETHERNETTAPFACTORY_HPP - -#include <vector> -#include <string> - -#include "../node/EthernetTapFactory.hpp" -#include "../node/Mutex.hpp" - -namespace ZeroTier { - -class WindowsEthernetTapFactory : public EthernetTapFactory -{ -public: - class Env - { - public: - Env(); - BOOL is64Bit; // true if WIN64 or WoW64 (32-bit binary on 64-bit architecture) - const char *devcon; // name of devcon binary in pathToHelpers to use - const char *tapDriver; // relative path to driver under pathToHelpers to use - }; - - /** - * Constants related to Windows environment, computed on program start - */ - static const Env WINENV; - - /** - * @param pathToHelpers Path to devcon32.exe, devcon64.exe, and other required helper binaries (ZeroTier running directory) - */ - WindowsEthernetTapFactory(const char *pathToHelpers); - virtual ~WindowsEthernetTapFactory(); - - virtual EthernetTap *open( - const MAC &mac, - unsigned int mtu, - unsigned int metric, - uint64_t nwid, - const char *desiredDevice, - const char *friendlyName, - void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), - void *arg); - virtual void close(EthernetTap *tap,bool destroyPersistentDevices); - - /** - * Uninstalls all persistent tap devices in the system belonging to ZeroTier - * - * This is for uninstallation. Do not call this while tap devices are active. - */ - static void destroyAllPersistentTapDevices(const char *pathToHelpers); - -private: - static void _deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId); - - std::string _pathToHelpers; - std::vector<EthernetTap *> _devices; - Mutex _devices_m; -}; - -} // namespace ZeroTier - -#endif |