Bunch of tap stuff, IP address assignment hookups, etc.

This commit is contained in:
Adam Ierymenko 2015-04-14 17:57:51 -07:00
parent e205e5fdfe
commit 67f1f1892f
16 changed files with 227 additions and 1648 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -456,6 +456,11 @@ typedef struct
*/ */
int portError; int portError;
/**
* Is this network enabled? If not, all frames to/from are dropped.
*/
int enabled;
/** /**
* Network config revision as reported by netconf master * Network config revision as reported by netconf master
* *

View file

@ -154,11 +154,6 @@
*/ */
#define ZT_IF_MTU ZT1_MAX_MTU #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 * Maximum number of packet fragments we'll support
* *
@ -313,14 +308,14 @@
#define ZT_ANTIRECURSION_HISTORY_SIZE 16 #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 #define ZT_BEACON_INTERVAL 30000
/** /**
* Do not respond to any beacon more often than this * 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 * Sanity limit on maximum bridge routes

View file

@ -436,7 +436,12 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
void Network::setEnabled(bool enabled) void Network::setEnabled(bool enabled)
{ {
Mutex::Lock _l(_lock); 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() 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->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->broadcastEnabled = (_config) ? (_config->enableBroadcast() ? 1 : 0) : 0;
ec->portError = _portError; ec->portError = _portError;
ec->enabled = (_enabled) ? 1 : 0;
ec->netconfRevision = (_config) ? (unsigned long)_config->revision() : 0; ec->netconfRevision = (_config) ? (unsigned long)_config->revision() : 0;
ec->multicastSubscriptionCount = std::min((unsigned int)_myMulticastGroups.size(),(unsigned int)ZT1_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS); ec->multicastSubscriptionCount = std::min((unsigned int)_myMulticastGroups.size(),(unsigned int)ZT1_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS);

View file

@ -348,7 +348,7 @@ private:
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
uint64_t _id; uint64_t _id;
MAC _mac; // local MAC address 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::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 std::map< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups bridged to us and when we last saw activity on each

View file

@ -263,6 +263,7 @@ static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp)
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#include "../node/Mutex.hpp" #include "../node/Mutex.hpp"
#include "OSUtils.hpp"
#include "OSXEthernetTap.hpp" #include "OSXEthernetTap.hpp"
// ff:ff:ff:ff:ff:ff with no ADI // 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 { namespace ZeroTier {
static long globalTapsRunning = 0;
static Mutex globalTapCreateLock;
OSXEthernetTap::OSXEthernetTap( OSXEthernetTap::OSXEthernetTap(
const char *homePath,
const MAC &mac, const MAC &mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric, unsigned int metric,
uint64_t nwid, uint64_t nwid,
const char *desiredDevice,
const char *friendlyName, 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) : void *arg) :
_handler(handler), _handler(handler),
_arg(arg), _arg(arg),
_nwid(nwid),
_homePath(homePath),
_mtu(mtu), _mtu(mtu),
_metric(metric), _metric(metric),
_fd(0), _fd(0),
_enabled(true) _enabled(true)
{ {
static Mutex globalTapCreateLock;
char devpath[64],ethaddr[64],mtustr[32],metstr[32]; char devpath[64],ethaddr[64],mtustr[32],metstr[32];
struct stat stattmp; struct stat stattmp;
@ -335,11 +340,29 @@ OSXEthernetTap::OSXEthernetTap(
if (mtu > 2800) if (mtu > 2800)
throw std::runtime_error("max tap MTU is 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. // Try to reopen the last device we had, if we had one and it's still unused.
bool recalledDevice = false; bool recalledDevice = false;
/*
if ((desiredDevice)&&(desiredDevice[0] == 'z')&&(desiredDevice[1] == 't')) { if ((desiredDevice)&&(desiredDevice[0] == 'z')&&(desiredDevice[1] == 't')) {
if ((strchr(desiredDevice,'/'))||(strchr(desiredDevice,'.'))) // security sanity check if ((strchr(desiredDevice,'/'))||(strchr(desiredDevice,'.'))) // security sanity check
throw std::runtime_error("invalid desiredDevice parameter"); 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. // Open the first unused tap device if we didn't recall a previous one.
if (!recalledDevice) { if (!recalledDevice) {
@ -402,15 +426,37 @@ OSXEthernetTap::OSXEthernetTap(
::pipe(_shutdownSignalPipe); ::pipe(_shutdownSignalPipe);
_thread = Thread::start(this); _thread = Thread::start(this);
++globalTapsRunning;
} }
OSXEthernetTap::~OSXEthernetTap() OSXEthernetTap::~OSXEthernetTap()
{ {
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
Thread::join(_thread); Thread::join(_thread);
::close(_fd); ::close(_fd);
::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[0]);
::close(_shutdownSignalPipe[1]); ::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) 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 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) if (!ip)
return false; return false;
std::set<InetAddress> allIps(ips()); std::vector<InetAddress> allIps(ips());
if (allIps.count(ip) > 0) if (std::binary_search(allIps.begin(),allIps.end(),ip))
return true; // IP/netmask already assigned return true;
// Remove and reconfigure if address is the same but netmask is different // 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 ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) {
if (___removeIp(_dev,*i)) if (___removeIp(_dev,*i))
break; break;
@ -463,26 +509,30 @@ bool OSXEthernetTap::addIP(const InetAddress &ip)
int exitcode = -1; int exitcode = -1;
::waitpid(cpid,&exitcode,0); ::waitpid(cpid,&exitcode,0);
return (exitcode == 0); return (exitcode == 0);
} } // else return false...
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)) if (___removeIp(_dev,ip))
return true; return true;
} }
return false; return false;
} }
std::set<InetAddress> OSXEthernetTap::ips() const std::vector<InetAddress> OSXEthernetTap::ips() const
{ {
struct ifaddrs *ifa = (struct ifaddrs *)0; struct ifaddrs *ifa = (struct ifaddrs *)0;
if (getifaddrs(&ifa)) if (getifaddrs(&ifa))
return std::set<InetAddress>(); return std::vector<InetAddress>();
std::set<InetAddress> r; std::vector<InetAddress> r;
struct ifaddrs *p = ifa; struct ifaddrs *p = ifa;
while (p) { while (p) {
@ -491,14 +541,14 @@ std::set<InetAddress> OSXEthernetTap::ips() const
case AF_INET: { case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; 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; } break;
case AF_INET6: { case AF_INET6: {
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr;
struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask;
uint32_t b[4]; uint32_t b[4];
memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); 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; } break;
} }
} }
@ -508,6 +558,9 @@ std::set<InetAddress> OSXEthernetTap::ips() const
if (ifa) if (ifa)
freeifaddrs(ifa); freeifaddrs(ifa);
std::sort(r.begin(),r.end());
std::unique(r.begin(),r.end());
return r; 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; struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0;
if (!_intl_getifmaddrs(&ifmap)) { if (!_intl_getifmaddrs(&ifmap)) {
struct _intl_ifmaddrs *p = 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 *in = (struct sockaddr_dl *)p->ifma_name;
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; 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))) 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; p = p->ifma_next;
} }
_intl_freeifmaddrs(ifmap); _intl_freeifmaddrs(ifmap);
} }
{ std::vector<InetAddress> allIps(ips());
std::set<InetAddress> allIps(ips()); for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i) newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
std::sort(newGroups.begin(),newGroups.end());
std::unique(newGroups.begin(),newGroups.end());
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::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
removed.push_back(*m);
} }
bool changed = false; _multicastGroups.swap(newGroups);
for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
if (!groups.count(*mg)) {
groups.insert(*mg);
changed = true;
}
}
for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) {
groups.erase(mg++);
changed = true;
} else ++mg;
}
return changed;
} }
void OSXEthernetTap::threadMain() void OSXEthernetTap::threadMain()
@ -582,7 +631,6 @@ void OSXEthernetTap::threadMain()
MAC to,from; MAC to,from;
int n,nfds,r; int n,nfds,r;
char getBuf[8194]; char getBuf[8194];
Buffer<4096> data;
// Wait for a moment after startup -- wait for Network to finish // Wait for a moment after startup -- wait for Network to finish
// constructing itself. // constructing itself.
@ -619,8 +667,8 @@ void OSXEthernetTap::threadMain()
to.setTo(getBuf,6); to.setTo(getBuf,6);
from.setTo(getBuf + 6,6); from.setTo(getBuf + 6,6);
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
data.copyFrom(getBuf + 14,(unsigned int)r - 14); // TODO: VLAN support
_handler(_arg,from,to,etherType,data); _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
} }
r = 0; r = 0;

View file

@ -32,10 +32,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <vector>
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/MAC.hpp" #include "../node/MAC.hpp"
#include "../node/Buffer.hpp"
#include "../node/InetAddress.hpp" #include "../node/InetAddress.hpp"
#include "../node/MulticastGroup.hpp" #include "../node/MulticastGroup.hpp"
@ -50,35 +51,38 @@ class OSXEthernetTap
{ {
public: public:
OSXEthernetTap( OSXEthernetTap(
const char *homePath,
const MAC &mac, const MAC &mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric, unsigned int metric,
uint64_t nwid, uint64_t nwid,
const char *desiredDevice,
const char *friendlyName, 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); void *arg);
~OSXEthernetTap(); ~OSXEthernetTap();
void setEnabled(bool en); void setEnabled(bool en);
bool enabled() const; bool enabled() const;
bool addIP(const InetAddress &ip); bool addIp(const InetAddress &ip);
bool removeIP(const InetAddress &ip); bool removeIp(const InetAddress &ip);
std::set<InetAddress> ips() const; std::vector<InetAddress> ips() const;
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
std::string deviceName() const; std::string deviceName() const;
void setFriendlyName(const char *friendlyName); void setFriendlyName(const char *friendlyName);
bool updateMulticastGroups(std::set<MulticastGroup> &groups); void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
void threadMain() void threadMain()
throw(); throw();
private: 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; void *_arg;
uint64_t _nwid;
Thread _thread; Thread _thread;
std::string _homePath;
std::string _dev; std::string _dev;
std::vector<MulticastGroup> _multicastGroups;
unsigned int _mtu; unsigned int _mtu;
unsigned int _metric; unsigned int _metric;
int _fd; int _fd;

View file

@ -45,6 +45,7 @@
#include "../node/Node.hpp" #include "../node/Node.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#include "../node/InetAddress.hpp" #include "../node/InetAddress.hpp"
#include "../node/MAC.hpp"
#include "../osdep/Phy.hpp" #include "../osdep/Phy.hpp"
#include "../osdep/OSUtils.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_MESSAGE_SIZE (1024 * 1024 * 8)
#define ZT_MAX_HTTP_CONNECTIONS 64 #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 { namespace ZeroTier {
// Used to convert HTTP header names to ASCII lower case // 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 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 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 ShttpOnMessageBegin(http_parser *parser);
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length); static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
static int ShttpOnStatus(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; _nextBackgroundTaskDeadline = 0;
uint64_t lastTapMulticastGroupCheck = 0;
for(;;) { for(;;) {
_run_m.lock(); _run_m.lock();
if (!_run) { if (!_run) {
@ -220,13 +230,20 @@ public:
uint64_t now = OSUtils::now(); uint64_t now = OSUtils::now();
if (dl <= 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); _node->processBackgroundTasks(now,&_nextBackgroundTaskDeadline);
dl = _nextBackgroundTaskDeadline; dl = _nextBackgroundTaskDeadline;
now = OSUtils::now(); now = OSUtils::now();
} }
const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100; const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
//printf("polling: %lums timeout\n",delay);
_phy.poll(delay); _phy.poll(delay);
} }
} catch (std::exception &exc) { } catch (std::exception &exc) {
@ -244,6 +261,13 @@ public:
_phy.close(_httpConnections.begin()->first); _phy.close(_httpConnections.begin()->first);
} catch ( ... ) {} } 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; delete _controlPlane;
_controlPlane = (ControlPlane *)0; _controlPlane = (ControlPlane *)0;
delete _node; 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; 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) 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); Mutex::Lock _l(_taps_m);
fflush(stderr); 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) inline void onHttpRequestToServer(HttpConnection *htc)
@ -539,6 +627,17 @@ private:
return p; 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; const std::string _homePath;
Phy<OneImpl *> _phy; Phy<OneImpl *> _phy;
NetworkConfigMaster *_master; NetworkConfigMaster *_master;
@ -552,6 +651,7 @@ private:
uint64_t _nextBackgroundTaskDeadline; uint64_t _nextBackgroundTaskDeadline;
std::map< uint64_t,EthernetTap * > _taps; 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; Mutex _taps_m;
std::map< PhySocket *,HttpConnection > _httpConnections; // no mutex for this since it's done in the main loop thread only 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) 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); } { 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) static int ShttpOnMessageBegin(http_parser *parser)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);

View file

@ -112,7 +112,7 @@ public:
virtual std::string fatalErrorMessage() const = 0; 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; virtual std::string portDeviceName(uint64_t nwid) const = 0;