diff options
-rw-r--r-- | attic/BSDEthernetTapFactory.cpp | 78 | ||||
-rw-r--r-- | attic/BSDEthernetTapFactory.hpp | 63 | ||||
-rw-r--r-- | attic/LinuxEthernetTapFactory.cpp | 74 | ||||
-rw-r--r-- | attic/LinuxEthernetTapFactory.hpp | 63 | ||||
-rw-r--r-- | attic/NativeSocketManager.cpp | 989 | ||||
-rw-r--r-- | attic/NativeSocketManager.hpp | 117 | ||||
-rw-r--r-- | attic/OSXEthernetTapFactory.cpp | 122 | ||||
-rw-r--r-- | attic/OSXEthernetTapFactory.hpp | 76 | ||||
-rw-r--r-- | include/ZeroTierOne.h | 5 | ||||
-rw-r--r-- | node/Constants.hpp | 9 | ||||
-rw-r--r-- | node/Network.cpp | 8 | ||||
-rw-r--r-- | node/Network.hpp | 2 | ||||
-rw-r--r-- | osdep/OSXEthernetTap.cpp | 130 | ||||
-rw-r--r-- | osdep/OSXEthernetTap.hpp | 20 | ||||
-rw-r--r-- | service/One.cpp | 111 | ||||
-rw-r--r-- | service/One.hpp | 2 |
16 files changed, 224 insertions, 1645 deletions
diff --git a/attic/BSDEthernetTapFactory.cpp b/attic/BSDEthernetTapFactory.cpp deleted file mode 100644 index 79ae73f7..00000000 --- a/attic/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/attic/BSDEthernetTapFactory.hpp b/attic/BSDEthernetTapFactory.hpp deleted file mode 100644 index 5c681fb4..00000000 --- a/attic/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 "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/attic/LinuxEthernetTapFactory.cpp b/attic/LinuxEthernetTapFactory.cpp deleted file mode 100644 index 014c6514..00000000 --- a/attic/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/attic/LinuxEthernetTapFactory.hpp b/attic/LinuxEthernetTapFactory.hpp deleted file mode 100644 index e61863ed..00000000 --- a/attic/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/attic/NativeSocketManager.cpp b/attic/NativeSocketManager.cpp deleted file mode 100644 index 797764ef..00000000 --- a/attic/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/attic/NativeSocketManager.hpp b/attic/NativeSocketManager.hpp deleted file mode 100644 index 5db06d6a..00000000 --- a/attic/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/attic/OSXEthernetTapFactory.cpp b/attic/OSXEthernetTapFactory.cpp deleted file mode 100644 index 4cad8daa..00000000 --- a/attic/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/attic/OSXEthernetTapFactory.hpp b/attic/OSXEthernetTapFactory.hpp deleted file mode 100644 index 2f2ee527..00000000 --- a/attic/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/include/ZeroTierOne.h b/include/ZeroTierOne.h index fa4c48d6..a06aedee 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -457,6 +457,11 @@ typedef struct int portError; /** + * Is this network enabled? If not, all frames to/from are dropped. + */ + int enabled; + + /** * Network config revision as reported by netconf master * * If this is zero, it means we're still waiting for our netconf. diff --git a/node/Constants.hpp b/node/Constants.hpp index 4baa1ae7..ed1153d5 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -155,11 +155,6 @@ #define ZT_IF_MTU ZT1_MAX_MTU /** - * Default interface metric for ZeroTier taps -- should be higher than physical ports - */ -#define ZT_DEFAULT_IF_METRIC 32768 - -/** * Maximum number of packet fragments we'll support * * The actual spec allows 16, but this is the most we'll support right @@ -313,14 +308,14 @@ #define ZT_ANTIRECURSION_HISTORY_SIZE 16 /** - * How often to broadcast beacons over physical local LANs + * How often to send LAN beacons */ #define ZT_BEACON_INTERVAL 30000 /** * Do not respond to any beacon more often than this */ -#define ZT_MIN_BEACON_RESPONSE_INTERVAL (ZT_BEACON_INTERVAL / 32) +#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500 /** * Sanity limit on maximum bridge routes diff --git a/node/Network.cpp b/node/Network.cpp index 0575f48c..ba0ee984 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -436,7 +436,12 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now) void Network::setEnabled(bool enabled) { Mutex::Lock _l(_lock); - _enabled = enabled; + if (_enabled != enabled) { + _enabled = enabled; + ZT1_VirtualNetworkConfig ctmp; + _externalConfig(&ctmp); + _portError = RR->node->configureVirtualNetworkPort(_id,ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE,&ctmp); + } } void Network::destroy() @@ -478,6 +483,7 @@ void Network::_externalConfig(ZT1_VirtualNetworkConfig *ec) const ec->bridge = (_config) ? ((_config->allowPassiveBridging() || (std::find(_config->activeBridges().begin(),_config->activeBridges().end(),RR->identity.address()) != _config->activeBridges().end())) ? 1 : 0) : 0; ec->broadcastEnabled = (_config) ? (_config->enableBroadcast() ? 1 : 0) : 0; ec->portError = _portError; + ec->enabled = (_enabled) ? 1 : 0; ec->netconfRevision = (_config) ? (unsigned long)_config->revision() : 0; ec->multicastSubscriptionCount = std::min((unsigned int)_myMulticastGroups.size(),(unsigned int)ZT1_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS); diff --git a/node/Network.hpp b/node/Network.hpp index 6a41fa67..08d9e527 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -348,7 +348,7 @@ private: const RuntimeEnvironment *RR; uint64_t _id; MAC _mac; // local MAC address - volatile bool _enabled; + bool _enabled; std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to including those behind us (updated periodically) std::map< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups bridged to us and when we last saw activity on each diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp index b333156e..c30d3988 100644 --- a/osdep/OSXEthernetTap.cpp +++ b/osdep/OSXEthernetTap.cpp @@ -263,6 +263,7 @@ static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp) #include "../node/Constants.hpp" #include "../node/Utils.hpp" #include "../node/Mutex.hpp" +#include "OSUtils.hpp" #include "OSXEthernetTap.hpp" // ff:ff:ff:ff:ff:ff with no ADI @@ -311,23 +312,27 @@ static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptR namespace ZeroTier { +static long globalTapsRunning = 0; +static Mutex globalTapCreateLock; + OSXEthernetTap::OSXEthernetTap( + const char *homePath, 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 (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len), void *arg) : _handler(handler), _arg(arg), + _nwid(nwid), + _homePath(homePath), _mtu(mtu), _metric(metric), _fd(0), _enabled(true) { - static Mutex globalTapCreateLock; char devpath[64],ethaddr[64],mtustr[32],metstr[32]; struct stat stattmp; @@ -335,11 +340,29 @@ OSXEthernetTap::OSXEthernetTap( 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"); + + if (stat("/dev/zt0",&stattmp)) { + if (homePath) { + long kextpid = (long)vfork(); + if (kextpid == 0) { + ::chdir(homePath); + OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); + ::execl("/sbin/kextload","/sbin/kextload","-q","-repository",homePath,"tap.kext",(const char *)0); + ::_exit(-1); + } else if (kextpid > 0) { + int exitcode = -1; + ::waitpid(kextpid,&exitcode,0); + } + ::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 cannot load tap.kext"); + } + throw std::runtime_error("/dev/zt# tap devices do not exist and tap.kext not available"); + } // 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"); @@ -352,6 +375,7 @@ OSXEthernetTap::OSXEthernetTap( } } } + */ // Open the first unused tap device if we didn't recall a previous one. if (!recalledDevice) { @@ -402,15 +426,37 @@ OSXEthernetTap::OSXEthernetTap( ::pipe(_shutdownSignalPipe); _thread = Thread::start(this); + + ++globalTapsRunning; } OSXEthernetTap::~OSXEthernetTap() { ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit Thread::join(_thread); + ::close(_fd); ::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[1]); + + { + Mutex::Lock _gl(globalTapCreateLock); + if (--globalTapsRunning <= 0) { + globalTapsRunning = 0; // sanity check -- should not be possible + + char tmp[16384]; + sprintf(tmp,"%s/%s",_homePath.c_str(),"tap.kext"); + long kextpid = (long)vfork(); + if (kextpid == 0) { + OSUtils::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); + } + } + } } void OSXEthernetTap::setEnabled(bool en) @@ -438,17 +484,17 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip) return false; // never reached, make compiler shut up about return value } -bool OSXEthernetTap::addIP(const InetAddress &ip) +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 + std::vector<InetAddress> allIps(ips()); + if (std::binary_search(allIps.begin(),allIps.end(),ip)) + 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) { + for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) { if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { if (___removeIp(_dev,*i)) break; @@ -463,26 +509,30 @@ bool OSXEthernetTap::addIP(const InetAddress &ip) int exitcode = -1; ::waitpid(cpid,&exitcode,0); return (exitcode == 0); - } + } // else return false... + return false; } -bool OSXEthernetTap::removeIP(const InetAddress &ip) +bool OSXEthernetTap::removeIp(const InetAddress &ip) { - if (ips().count(ip) > 0) { + if (!ip) + return true; + std::vector<InetAddress> allIps(ips()); + if (!std::binary_search(allIps.begin(),allIps.end(),ip)) { if (___removeIp(_dev,ip)) return true; } return false; } -std::set<InetAddress> OSXEthernetTap::ips() const +std::vector<InetAddress> OSXEthernetTap::ips() const { struct ifaddrs *ifa = (struct ifaddrs *)0; if (getifaddrs(&ifa)) - return std::set<InetAddress>(); + return std::vector<InetAddress>(); - std::set<InetAddress> r; + std::vector<InetAddress> r; struct ifaddrs *p = ifa; while (p) { @@ -491,14 +541,14 @@ std::set<InetAddress> OSXEthernetTap::ips() const 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))); + r.push_back(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]))); + r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); } break; } } @@ -508,6 +558,9 @@ std::set<InetAddress> OSXEthernetTap::ips() const if (ifa) freeifaddrs(ifa); + std::sort(r.begin(),r.end()); + std::unique(r.begin(),r.end()); + return r; } @@ -533,9 +586,10 @@ void OSXEthernetTap::setFriendlyName(const char *friendlyName) { } -bool OSXEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) +void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed) { - std::set<MulticastGroup> newGroups; + std::vector<MulticastGroup> newGroups; + struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0; if (!_intl_getifmaddrs(&ifmap)) { struct _intl_ifmaddrs *p = ifmap; @@ -544,35 +598,30 @@ bool OSXEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) 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)); + newGroups.push_back(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)); - } + std::vector<InetAddress> allIps(ips()); + for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip) + newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); - bool changed = false; + std::sort(newGroups.begin(),newGroups.end()); + std::unique(newGroups.begin(),newGroups.end()); - for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) { - if (!groups.count(*mg)) { - groups.insert(*mg); - changed = true; - } + for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) { + if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) + added.push_back(*m); } - for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) { - if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) { - groups.erase(mg++); - changed = true; - } else ++mg; + for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { + if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) + removed.push_back(*m); } - return changed; + _multicastGroups.swap(newGroups); } void OSXEthernetTap::threadMain() @@ -582,7 +631,6 @@ void OSXEthernetTap::threadMain() 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. @@ -619,8 +667,8 @@ void OSXEthernetTap::threadMain() 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); + // TODO: VLAN support + _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14); } r = 0; diff --git a/osdep/OSXEthernetTap.hpp b/osdep/OSXEthernetTap.hpp index 24d81932..a37ddb6b 100644 --- a/osdep/OSXEthernetTap.hpp +++ b/osdep/OSXEthernetTap.hpp @@ -32,10 +32,11 @@ #include <stdlib.h> #include <stdexcept> +#include <string> +#include <vector> #include "../node/Constants.hpp" #include "../node/MAC.hpp" -#include "../node/Buffer.hpp" #include "../node/InetAddress.hpp" #include "../node/MulticastGroup.hpp" @@ -50,35 +51,38 @@ class OSXEthernetTap { public: OSXEthernetTap( + const char *homePath, 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 (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), void *arg); ~OSXEthernetTap(); void setEnabled(bool en); bool enabled() const; - bool addIP(const InetAddress &ip); - bool removeIP(const InetAddress &ip); - std::set<InetAddress> ips() const; + bool addIp(const InetAddress &ip); + bool removeIp(const InetAddress &ip); + std::vector<InetAddress> ips() const; void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); std::string deviceName() const; void setFriendlyName(const char *friendlyName); - bool updateMulticastGroups(std::set<MulticastGroup> &groups); + void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed); void threadMain() throw(); private: - void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); + void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); void *_arg; + uint64_t _nwid; Thread _thread; + std::string _homePath; std::string _dev; + std::vector<MulticastGroup> _multicastGroups; unsigned int _mtu; unsigned int _metric; int _fd; diff --git a/service/One.cpp b/service/One.cpp index 031dc375..6e9f0b0d 100644 --- a/service/One.cpp +++ b/service/One.cpp @@ -45,6 +45,7 @@ #include "../node/Node.hpp" #include "../node/Utils.hpp" #include "../node/InetAddress.hpp" +#include "../node/MAC.hpp" #include "../osdep/Phy.hpp" #include "../osdep/OSUtils.hpp" @@ -61,6 +62,12 @@ namespace ZeroTier { typedef OSXEthernetTap EthernetTap; } #define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 8) #define ZT_MAX_HTTP_CONNECTIONS 64 +// Interface metric for ZeroTier taps +#define ZT_IF_METRIC 32768 + +// How often to check for new multicast subscriptions on a tap device +#define ZT_TAP_CHECK_MULTICAST_INTERVAL 30000 + namespace ZeroTier { // Used to convert HTTP header names to ASCII lower case @@ -75,6 +82,8 @@ static int SnodeDataStorePutFunction(ZT1_Node *node,void *uptr,const char *name, static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,const struct sockaddr_storage *addr,unsigned int desperation,const void *data,unsigned int len); static void SnodeVirtualNetworkFrameFunction(ZT1_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); +static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); + static int ShttpOnMessageBegin(http_parser *parser); static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length); static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length); @@ -206,6 +215,7 @@ public: } _nextBackgroundTaskDeadline = 0; + uint64_t lastTapMulticastGroupCheck = 0; for(;;) { _run_m.lock(); if (!_run) { @@ -220,13 +230,20 @@ public: uint64_t now = OSUtils::now(); if (dl <= now) { + if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) { + lastTapMulticastGroupCheck = now; + Mutex::Lock _l(_taps_m); + for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t) + _updateMulticastGroups(t->first,t->second); + } + _node->processBackgroundTasks(now,&_nextBackgroundTaskDeadline); + dl = _nextBackgroundTaskDeadline; now = OSUtils::now(); } const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100; - //printf("polling: %lums timeout\n",delay); _phy.poll(delay); } } catch (std::exception &exc) { @@ -244,6 +261,13 @@ public: _phy.close(_httpConnections.begin()->first); } catch ( ... ) {} + { + Mutex::Lock _l(_taps_m); + for(std::map< uint64_t,EthernetTap * >::iterator t(_taps.begin());t!=_taps.end();++t) + delete t->second; + _taps.clear(); + } + delete _controlPlane; _controlPlane = (ControlPlane *)0; delete _node; @@ -365,8 +389,65 @@ public: } } - inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,enum ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nwconf) + inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,enum ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nwc) { + Mutex::Lock _l(_taps_m); + std::map< uint64_t,EthernetTap * >::iterator t(_taps.find(nwid)); + switch(op) { + case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_UP: + if (t == _taps.end()) { + try { + char friendlyName[1024]; + Utils::snprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid); + t = _taps.insert(std::pair< uint64_t,EthernetTap *>(nwid,new EthernetTap( + _homePath.c_str(), + MAC(nwc->mac), + nwc->mtu, + ZT_IF_METRIC, + nwid, + friendlyName, + StapFrameHandler, + (void *)this))).first; + } catch ( ... ) { + return -2; + } + } + // fall through... + case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: + if (t != _taps.end()) { + t->second->setEnabled(nwc->enabled != 0); + + std::vector<InetAddress> &assignedIps = _tapAssignedIps[nwid]; + std::vector<InetAddress> newAssignedIps; + for(unsigned int i=0;i<nwc->assignedAddressCount;++i) + newAssignedIps.push_back(InetAddress(nwc->assignedAddresses[i])); + std::sort(newAssignedIps.begin(),newAssignedIps.end()); + std::unique(newAssignedIps.begin(),newAssignedIps.end()); + for(std::vector<InetAddress>::iterator ip(newAssignedIps.begin());ip!=newAssignedIps.end();++ip) { + if (!std::binary_search(assignedIps.begin(),assignedIps.end(),*ip)) + t->second->addIp(*ip); + } + for(std::vector<InetAddress>::iterator ip(assignedIps.begin());ip!=assignedIps.end();++ip) { + if (!std::binary_search(newAssignedIps.begin(),newAssignedIps.end(),*ip)) + t->second->removeIp(*ip); + } + assignedIps.swap(newAssignedIps); + + _updateMulticastGroups(t->first,t->second); + if (nwc->broadcastEnabled) + _node->multicastSubscribe(nwid,0xffffffffffffULL,0); + else _node->multicastUnsubscribe(nwid,0xffffffffffffULL,0); + } + break; + case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN: + case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY: + if (t != _taps.end()) { + delete t->second; + _taps.erase(t); + _tapAssignedIps.erase(nwid); + } + break; + } return 0; } @@ -466,8 +547,15 @@ public: inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { - fprintf(stderr,"VIRTUAL NETWORK FRAME from %.16llx : %.12llx -> %.12llx %.4x %u bytes\n",nwid,sourceMac,destMac,etherType,len); - fflush(stderr); + Mutex::Lock _l(_taps_m); + std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.find(nwid)); + if (t != _taps.end()) + t->second->put(MAC(sourceMac),MAC(destMac),etherType,data,len); + } + + inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) + { + _node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline); } inline void onHttpRequestToServer(HttpConnection *htc) @@ -539,6 +627,17 @@ private: return p; } + void _updateMulticastGroups(uint64_t nwid,EthernetTap *tap) + { + // assumes _taps_m is locked + std::vector<MulticastGroup> added,removed; + tap->scanMulticastGroups(added,removed); + for(std::vector<MulticastGroup>::iterator m(added.begin());m!=added.end();++m) + _node->multicastSubscribe(nwid,m->mac().toInt(),m->adi()); + for(std::vector<MulticastGroup>::iterator m(removed.begin());m!=removed.end();++m) + _node->multicastUnsubscribe(nwid,m->mac().toInt(),m->adi()); + } + const std::string _homePath; Phy<OneImpl *> _phy; NetworkConfigMaster *_master; @@ -552,6 +651,7 @@ private: uint64_t _nextBackgroundTaskDeadline; std::map< uint64_t,EthernetTap * > _taps; + std::map< uint64_t,std::vector<InetAddress> > _tapAssignedIps; // ZeroTier assigned IPs, not user or dhcp assigned Mutex _taps_m; std::map< PhySocket *,HttpConnection > _httpConnections; // no mutex for this since it's done in the main loop thread only @@ -577,6 +677,9 @@ static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,const struct so static void SnodeVirtualNetworkFrameFunction(ZT1_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast<OneImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); } +static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) +{ reinterpret_cast<OneImpl *>(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); } + static int ShttpOnMessageBegin(http_parser *parser) { HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); diff --git a/service/One.hpp b/service/One.hpp index 48fbc2ba..953053f4 100644 --- a/service/One.hpp +++ b/service/One.hpp @@ -112,7 +112,7 @@ public: virtual std::string fatalErrorMessage() const = 0; /** - * @return System device name corresponding with a given ZeroTier network ID + * @return System device name corresponding with a given ZeroTier network ID or empty string if not opened yet or network ID not found */ virtual std::string portDeviceName(uint64_t nwid) const = 0; |