A whole bunch more service work.

This commit is contained in:
Adam Ierymenko 2021-02-11 00:35:34 -05:00
parent a321ee6c28
commit f9649217fb
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
24 changed files with 539 additions and 4686 deletions

View file

@ -246,9 +246,8 @@ if(NOT PACKAGE_STATIC)
endif(BUILD_CENTRAL_CONTROLLER)
add_subdirectory(core)
add_subdirectory(controller)
# add_subdirectory(controller)
add_subdirectory(osdep)
#add_subdirectory(serviceiocore)
#file(GLOB go_src
# ${CMAKE_SOURCE_DIR}/cmd/*.go

View file

@ -1,439 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/cdefs.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/route.h>
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include <utility>
#include "../core/Constants.hpp"
#include "../core/Utils.hpp"
#include "../core/Mutex.hpp"
#include "OSUtils.hpp"
#include "BSDEthernetTap.hpp"
#define ZT_BASE32_CHARS "0123456789abcdefghijklmnopqrstuv"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
namespace ZeroTier {
BSDEthernetTap::BSDEthernetTap(
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_handler(handler),
_arg(arg),
_nwid(nwid),
_mtu(mtu),
_metric(metric),
_fd(0),
_enabled(true)
{
static Mutex globalTapCreateLock;
char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32];
Mutex::Lock _gl(globalTapCreateLock);
#ifdef __FreeBSD__
/* FreeBSD allows long interface names and interface renaming */
_dev = "zt";
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 50) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 45) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 40) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 35) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 30) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 25) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 20) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 15) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 10) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]);
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
for(int i=9993;i<(9993+128);++i) {
OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) {
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"create",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
} else throw std::runtime_error("fork() failed");
struct stat stattmp;
if (!stat(devpath,&stattmp)) {
cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"name",_dev.c_str(),(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
if (exitcode)
throw std::runtime_error("ifconfig rename operation failed");
} else throw std::runtime_error("fork() failed");
_fd = ::open(devpath,O_RDWR);
if (_fd > 0)
break;
else throw std::runtime_error("unable to open created tap device");
} else {
throw std::runtime_error("cannot find /dev node for newly created tap device");
}
}
}
#else
/* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */
for(int i=0;i<64;++i) {
OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
_fd = ::open(devpath,O_RDWR);
if (_fd > 0) {
_dev = tmpdevname;
break;
}
}
#endif
if (_fd <= 0)
throw std::runtime_error("unable to open TAP device or no more devices available");
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
::close(_fd);
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
}
// Configure MAC address and MTU, bring interface up
OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
if (exitcode) {
::close(_fd);
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
}
}
// Set close-on-exec so that devices cannot persist if we fork/exec for update
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
::pipe(_shutdownSignalPipe);
_thread = Thread::start(this);
}
BSDEthernetTap::~BSDEthernetTap()
{
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
Thread::join(_thread);
::close(_fd);
::close(_shutdownSignalPipe[0]);
::close(_shutdownSignalPipe[1]);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
}
}
void BSDEthernetTap::setEnabled(bool en)
{
_enabled = en;
}
bool BSDEthernetTap::enabled() const
{
return _enabled;
}
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
{
long cpid = (long)vfork();
if (cpid == 0) {
char ipbuf[64];
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString(ipbuf),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
waitpid(cpid,&exitcode,0);
return (exitcode == 0);
}
return false; // never reached, make compiler shut up about return value
}
bool BSDEthernetTap::addIp(const InetAddress &ip)
{
if (!ip)
return false;
std::vector<InetAddress> allIps(ips());
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end())
return true; // IP/netmask already assigned
// Remove and reconfigure if address is the same but netmask is different
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;
}
}
long cpid = (long)vfork();
if (cpid == 0) {
char tmp[128];
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString(tmp),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
return (exitcode == 0);
}
return false;
}
bool BSDEthernetTap::removeIp(const InetAddress &ip)
{
if (!ip)
return false;
std::vector<InetAddress> allIps(ips());
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) {
if (___removeIp(_dev,ip))
return true;
}
return false;
}
std::vector<InetAddress> BSDEthernetTap::ips() const
{
struct ifaddrs *ifa = (struct ifaddrs *)0;
if (getifaddrs(&ifa))
return std::vector<InetAddress>();
std::vector<InetAddress> r;
struct ifaddrs *p = ifa;
while (p) {
if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) {
switch(p->ifa_addr->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask;
r.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.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;
}
}
p = p->ifa_next;
}
if (ifa)
freeifaddrs(ifa);
std::sort(r.begin(),r.end());
std::unique(r.begin(),r.end());
return r;
}
void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
uint8_t putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf);
from.copyTo(putBuf + 6);
Utils::storeBigEndian(putBuf + 12, (uint16_t)etherType);
Utils::copy(putBuf + 14,data,len);
len += 14;
::write(_fd,putBuf,len);
}
}
std::string BSDEthernetTap::deviceName() const
{
return _dev;
}
void BSDEthernetTap::setFriendlyName(const char *friendlyName)
{
}
void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
{
std::vector<MulticastGroup> newGroups;
#ifndef __OpenBSD__
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
if (!getifmaddrs(&ifmap)) {
struct ifmaddrs *p = ifmap;
while (p) {
if (p->ifma_addr->sa_family == AF_LINK) {
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
newGroups.push_back(MulticastGroup(MAC((const uint8_t *)(la->sdl_data + la->sdl_nlen)),0));
}
p = p->ifma_next;
}
freeifmaddrs(ifmap);
}
#endif // __OpenBSD__
std::vector<InetAddress> allIps(ips());
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
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);
}
_multicastGroups.swap(newGroups);
}
void BSDEthernetTap::setMtu(unsigned int mtu)
{
if (mtu != _mtu) {
_mtu = mtu;
long cpid = (long)vfork();
if (cpid == 0) {
char tmp[64];
OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
waitpid(cpid,&exitcode,0);
}
}
}
void BSDEthernetTap::threadMain()
throw()
{
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
uint8_t getBuf[ZT_MAX_MTU + 64];
// Wait for a moment after startup -- wait for Network to finish
// constructing itself.
Thread::sleep(500);
FD_ZERO(&readfds);
FD_ZERO(&nullfds);
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
r = 0;
for(;;) {
FD_SET(_shutdownSignalPipe[0],&readfds);
FD_SET(_fd,&readfds);
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
break;
if (FD_ISSET(_fd,&readfds)) {
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
if (n < 0) {
if ((errno != EINTR)&&(errno != ETIMEDOUT))
break;
} else {
// Some tap drivers like to send the ethernet frame and the
// payload in two chunks, so handle that by accumulating
// data until we have at least a frame.
r += n;
if (r > 14) {
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
r = _mtu + 14;
if (_enabled) {
to.setTo(getBuf);
from.setTo(getBuf + 6);
_handler(_arg,(void *)0,_nwid,from,to,Utils::loadBigEndian<uint16_t>(getBuf + 12),0,(const void *)(getBuf + 14),r - 14);
}
r = 0;
}
}
}
}
}
} // namespace ZeroTier

View file

@ -1,77 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_BSDETHERNETTAP_HPP
#define ZT_BSDETHERNETTAP_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <stdexcept>
#include "../core/Constants.hpp"
#include "../core/MulticastGroup.hpp"
#include "../core/MAC.hpp"
#include "Thread.hpp"
#include "EthernetTap.hpp"
namespace ZeroTier {
class BSDEthernetTap : public EthernetTap
{
public:
BSDEthernetTap(
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
virtual ~BSDEthernetTap();
virtual void setEnabled(bool en);
virtual bool enabled() const;
virtual bool addIp(const InetAddress &ip);
virtual bool removeIp(const InetAddress &ip);
virtual std::vector<InetAddress> ips() const;
virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
virtual std::string deviceName() const;
virtual void setFriendlyName(const char *friendlyName);
virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
virtual void setMtu(unsigned int mtu);
void threadMain()
throw();
private:
void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
uint64_t _nwid;
Thread _thread;
std::string _dev;
std::vector<MulticastGroup> _multicastGroups;
unsigned int _mtu;
unsigned int _metric;
int _fd;
int _shutdownSignalPipe[2];
volatile bool _enabled;
};
} // namespace ZeroTier
#endif

View file

@ -2,32 +2,32 @@ cmake_minimum_required(VERSION 3.0)
project(zt_osdep)
set(src
ManagedRoute.cpp
# ManagedRoute.cpp
OSUtils.cpp
rust-osdep.cpp
)
set(headers
ManagedRoute.hpp
# ManagedRoute.hpp
OSUtils.hpp
Thread.hpp
rust-osdep.h
)
if(WIN32)
set(src ${src} WindowsEthernetTap.cpp)
set(headers ${headers} WindowsEthernetTap.hpp)
elseif(UNIX)
if(CMAKE_SYSTEM_NAME MATCHES "BSD")
set(src ${src} BSDEthernetTap.cpp)
set(headers ${headers} BSDEthernetTap.hpp)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set(src ${src} freebsd_getifmaddrs.c)
set(headers ${headers} freebsd_getifmaddrs.h)
endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
endif(WIN32)
#if(WIN32)
# set(src ${src} WindowsEthernetTap.cpp)
# set(headers ${headers} WindowsEthernetTap.hpp)
#elseif(UNIX)
# if(CMAKE_SYSTEM_NAME MATCHES "BSD")
# set(src ${src} BSDEthernetTap.cpp)
# set(headers ${headers} BSDEthernetTap.hpp)
# endif()
#
# if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
# set(src ${src} freebsd_getifmaddrs.c)
# set(headers ${headers} freebsd_getifmaddrs.h)
# endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
#endif(WIN32)
add_library(${PROJECT_NAME} STATIC ${src} ${headers})
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)

View file

@ -1,139 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#include "EthernetTap.hpp"
#include "OSUtils.hpp"
#ifdef ZT_SDK
#include "../controller/EmbeddedNetworkController.hpp"
#include "../core/Node.hpp"
#include "../include/VirtualTap.hpp"
#else
#ifdef __APPLE__
#include <sys/sysctl.h>
#include "MacEthernetTap.hpp"
#include "MacKextEthernetTap.hpp"
#endif // __APPLE__
#ifdef __LINUX__
#include "LinuxEthernetTap.hpp"
#endif // __LINUX__
#ifdef __WINDOWS__
#include "WindowsEthernetTap.hpp"
#endif // __WINDOWS__
#ifdef __FreeBSD__
#include "BSDEthernetTap.hpp"
#endif // __FreeBSD__
#ifdef __NetBSD__
#include "NetBSDEthernetTap.hpp"
#endif // __NetBSD__
#ifdef __OpenBSD__
#include "BSDEthernetTap.hpp"
#endif // __OpenBSD__
#endif
namespace ZeroTier {
std::shared_ptr<EthernetTap> EthernetTap::newInstance(
const char *tapDeviceType, // OS-specific, NULL for default
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg)
{
#ifdef ZT_SDK
return std::shared_ptr<EthernetTap>(new VirtualTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
#else // not ZT_SDK
#ifdef __APPLE__
char osrelease[256];
size_t size = sizeof(osrelease);
if (sysctlbyname("kern.osrelease",osrelease,&size,nullptr,0) == 0) {
char *dotAt = strchr(osrelease,'.');
if (dotAt) {
*dotAt = (char)0;
// The "feth" virtual Ethernet device type appeared in Darwin 17.x.x. Older versions
// (Sierra and earlier) must use the a kernel extension.
if (strtol(osrelease,(char **)0,10) < 17) {
return std::shared_ptr<EthernetTap>(new MacKextEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
} else {
return std::shared_ptr<EthernetTap>(new MacEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
}
}
}
#endif // __APPLE__
#ifdef __LINUX__
return std::shared_ptr<EthernetTap>(new LinuxEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
#endif // __LINUX__
#ifdef __WINDOWS__
return std::shared_ptr<EthernetTap>(new WindowsEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
#endif // __WINDOWS__
#ifdef __FreeBSD__
return std::shared_ptr<EthernetTap>(new BSDEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
#endif // __FreeBSD__
#ifdef __NetBSD__
return std::shared_ptr<EthernetTap>(new NetBSDEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
#endif // __NetBSD__
#ifdef __OpenBSD__
return std::shared_ptr<EthernetTap>(new BSDEthernetTap(homePath,mac,mtu,metric,nwid,friendlyName,handler,arg));
#endif // __OpenBSD__
#endif // ZT_SDK?
return std::shared_ptr<EthernetTap>();
}
EthernetTap::EthernetTap() {}
EthernetTap::~EthernetTap() {}
bool EthernetTap::addIps(std::vector<InetAddress> ips)
{
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
if (!addIp(*i))
return false;
}
return true;
}
std::string EthernetTap::routingDeviceName() const
{
#ifdef __WINDOWS__
char tapdev[64];
OSUtils::ztsnprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)(((const WindowsEthernetTap *)(this))->luid().Value));
return std::string(tapdev);
#else
return this->deviceName();
#endif
}
} // namespace ZeroTier

View file

@ -1,63 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_ETHERNETTAP_HPP
#define ZT_ETHERNETTAP_HPP
#include "../core/Constants.hpp"
#include "../core/MAC.hpp"
#include "../core/InetAddress.hpp"
#include "../core/MulticastGroup.hpp"
#include <string>
#include <memory>
#include <vector>
#include <mutex>
#include <map>
namespace ZeroTier {
class EthernetTap
{
public:
static std::shared_ptr<EthernetTap> newInstance(
const char *tapDeviceType, // OS-specific, NULL for default
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
EthernetTap();
virtual ~EthernetTap();
virtual void setEnabled(bool en) = 0;
virtual bool enabled() const = 0;
virtual bool addIp(const InetAddress &ip) = 0;
virtual bool addIps(std::vector<InetAddress> ips); // uses addIp() unless overridden
virtual bool removeIp(const InetAddress &ip) = 0;
virtual std::vector<InetAddress> ips() const = 0;
virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) = 0;
virtual std::string deviceName() const = 0;
virtual std::string routingDeviceName() const;
virtual void setFriendlyName(const char *friendlyName) = 0;
virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed) = 0;
virtual void setMtu(unsigned int mtu) = 0;
};
} // namespace ZeroTier
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,152 +0,0 @@
/*
* Copyright (c)2019 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_LINUX_NETLINK_HPP
#define ZT_LINUX_NETLINK_HPP
#include "../node/Constants.hpp"
#ifdef __LINUX__
#include <vector>
#include <map>
#include <set>
#include <sys/socket.h>
#include <asm/types.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
//#include <linux/if.h>
#include "../node/InetAddress.hpp"
#include "../node/MAC.hpp"
#include "Thread.hpp"
#include "../node/Hashtable.hpp"
#include "../node/Mutex.hpp"
namespace ZeroTier {
/**
* Interface with Linux's RTNETLINK
*/
class LinuxNetLink
{
private:
LinuxNetLink();
~LinuxNetLink();
public:
struct Route {
InetAddress target;
InetAddress via;
InetAddress src;
int ifidx;
inline bool operator==(const Route &r) const
{ return ((target == r.target)&&(via == r.via)&&(src == r.src)&&(ifidx == r.ifidx)); }
inline bool operator!=(const Route &r) const
{ return (!(*this == r)); }
inline bool operator<(const Route &r) const
{
if (target < r.target) {
return true;
} else if (target == r.target) {
if (via < r.via) {
return true;
} else if (via == r.via) {
if (src < r.src) {
return true;
} else if (src == r.src) {
return (ifidx < r.ifidx);
}
}
}
return false;
}
inline bool operator>(const Route &r) const
{ return (r < *this); }
inline bool operator<=(const Route &r) const
{ return !(r < *this); }
inline bool operator>=(const Route &r) const
{ return !(*this < r); }
};
static LinuxNetLink& getInstance()
{
static LinuxNetLink instance;
return instance;
}
LinuxNetLink(LinuxNetLink const&) = delete;
void operator=(LinuxNetLink const&) = delete;
void addRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName);
void delRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName);
void addAddress(const InetAddress &addr, const char *iface);
void removeAddress(const InetAddress &addr, const char *iface);
bool routeIsSet(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifname);
void threadMain() throw();
private:
int _doRecv(int fd);
void _processMessage(struct nlmsghdr *nlp, int nll);
void _routeAdded(struct nlmsghdr *nlp);
void _routeDeleted(struct nlmsghdr *nlp);
void _linkAdded(struct nlmsghdr *nlp);
void _linkDeleted(struct nlmsghdr *nlp);
void _ipAddressAdded(struct nlmsghdr *nlp);
void _ipAddressDeleted(struct nlmsghdr *nlp);
void _requestInterfaceList();
void _requestIPv4Routes();
void _requestIPv6Routes();
int _indexForInterface(const char *iface);
void _setSocketTimeout(int fd, int seconds = 1);
Thread _t;
bool _running;
uint32_t _seq;
std::map< InetAddress,std::set<LinuxNetLink::Route> > _routes;
Mutex _routes_m;
struct iface_entry {
iface_entry()
{ memset(this,0,sizeof(iface_entry)); }
int index;
char ifacename[16]; // IFNAMSIZ on Linux == 16
char mac[18];
char mac_bin[6];
unsigned int mtu;
};
Hashtable<int, iface_entry> _interfaces;
Mutex _if_m;
// socket communication vars;
int _fd;
struct sockaddr_nl _la;
};
}
#endif
#endif // ZT_LINUX_NETLINK_HPPS

View file

@ -49,10 +49,11 @@
#include <utility>
#include "ManagedRoute.hpp"
#ifdef __LINUX__
#include "LinuxNetLink.hpp"
#endif
#define ZT_BSD_ROUTE_CMD "/sbin/route"
#define ZT_LINUX_IP_COMMAND "/sbin/ip"
#define ZT_LINUX_IP_COMMAND_2 "/usr/sbin/ip"
namespace ZeroTier {
@ -269,26 +270,7 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress
#ifdef __LINUX__ // ----------------------------------------------------------
#define ZT_ROUTING_SUPPORT_FOUND 1
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *localInterface)
{
long p = (long)fork();
if (p > 0) {
int exitcode = -1;
::waitpid(p,&exitcode,0);
} else if (p == 0) {
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
char ipbuf[64],ipbuf2[64];
if (via) {
::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0);
::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0);
} else if ((localInterface)&&(localInterface[0])) {
::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0);
::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0);
}
::_exit(-1);
}
}
// This has been replaced by LinuxNetLink
#endif // __LINUX__ ----------------------------------------------------------
@ -393,15 +375,19 @@ ManagedRoute::ManagedRoute(const InetAddress &target,const InetAddress &via,cons
_target = target;
_via = via;
_src = src;
if (via.ss_family == AF_INET)
if (_via.ss_family == AF_INET) {
_via.setPort(32);
else if (via.ss_family == AF_INET6)
} else if (_via.ss_family == AF_INET6) {
_via.setPort(128);
if (src.ss_family == AF_INET) {
}
if (_src.ss_family == AF_INET) {
_src.setPort(32);
} else if (src.ss_family == AF_INET6) {
} else if (_src.ss_family == AF_INET6) {
_src.setPort(128);
}
Utils::scopy(_device,sizeof(_device),device);
_systemDevice[0] = (char)0;
}
@ -508,13 +494,14 @@ bool ManagedRoute::sync()
#ifdef __LINUX__ // ----------------------------------------------------------
if (!_applied.count(leftt)) {
const char *const devptr = (_via) ? (const char *)0 : _device;
if ((leftt)&&(!LinuxNetLink::getInstance().routeIsSet(leftt,_via,_src,devptr))) {
_applied[leftt] = false; // boolean unused
_routeCmd("replace",leftt,_via,(_via) ? (const char *)0 : _device);
LinuxNetLink::getInstance().addRoute(leftt, _via, _src, devptr);
}
if ((rightt)&&(!_applied.count(rightt))) {
if ((rightt)&&(!LinuxNetLink::getInstance().routeIsSet(rightt,_via,_src,devptr))) {
_applied[rightt] = false; // boolean unused
_routeCmd("replace",rightt,_via,(_via) ? (const char *)0 : _device);
LinuxNetLink::getInstance().addRoute(rightt, _via, _src, devptr);
}
#endif // __LINUX__ ----------------------------------------------------------
@ -562,7 +549,8 @@ void ManagedRoute::remove()
#endif // __BSD__ ------------------------------------------------------------
#ifdef __LINUX__ // ----------------------------------------------------------
_routeCmd("del",r->first,_via,(_via) ? (const char *)0 : _device);
//_routeCmd("del",r->first,_via,(_via) ? (const char *)0 : _device);
LinuxNetLink::getInstance().delRoute(r->first,_via,_src,(_via) ? (const char *)0 : _device);
#endif // __LINUX__ ----------------------------------------------------------
#ifdef __WINDOWS__ // --------------------------------------------------------

View file

@ -1,479 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/cdefs.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/route.h>
#include <sys/sysctl.h>
#include "freebsd_getifmaddrs.h"
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include <utility>
#include "../core/Constants.hpp"
#include "../core/Utils.hpp"
#include "../core/Mutex.hpp"
#include "OSUtils.hpp"
#include "NetBSDEthernetTap.hpp"
#include <iostream>
using namespace std;
#define ZT_BASE32_CHARS "0123456789abcdefghijklmnopqrstuv"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
namespace ZeroTier {
NetBSDEthernetTap::NetBSDEthernetTap(
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_handler(handler),
_arg(arg),
_nwid(nwid),
_mtu(mtu),
_metric(metric),
_fd(0),
_enabled(true)
{
static Mutex globalTapCreateLock;
char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32];
struct stat stattmp;
Mutex::Lock _gl(globalTapCreateLock);
if (mtu > 2800)
throw std::runtime_error("max tap MTU is 2800");
// we can create /dev/tap*
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
for(int i=9993;i<(9993+128);++i) {
Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) {
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"create",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
} else throw std::runtime_error("fork() failed");
cpid = (long)vfork();
if (cpid == 0) {
string tmp;
sprintf((char*)tmp.c_str(), "%d", i);
string minor = tmp.c_str();
::execl("/sbin/mknod","/sbin/mknod",devpath,"c","169",minor.c_str(),(const char *)0);
// http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/conf/majors
// major 169 => tap
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
} else throw std::runtime_error("fork() failed");
cerr<<"created device "<<devpath<<endl;
_dev = tmpdevname;
_fd = ::open( devpath,O_RDWR);
if (!stat(devpath,&stattmp)) {
if (_fd > 0)
break;
else
throw std::runtime_error("unable to open created tap device ");
} else {
throw std::runtime_error("cannot find /dev node for newly created tap device");
}
}
}
if (_fd <= 0)
throw std::runtime_error("unable to open TAP device or no more devices available");
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
::close(_fd);
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
}
// Configure MAC address and MTU, bring interface up
Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"link",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
if (exitcode) {
::close(_fd);
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
}
}
// ifconfig link seems to be different from address
// https://wiki.netbsd.org/tutorials/faking_a_mac_address/
cpid = (long)vfork();
if (cpid == 0) {
string cmdline = "net.link.tap."+string(_dev);
cmdline += "="+string(ethaddr);
::execl("/sbin/sysctl","/sbin/sysctl","-w",cmdline.c_str(),(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
if (exitcode) {
::close(_fd);
throw std::runtime_error("sysctl failure setting link-layer address and activating tap interface");
}
}
// Set close-on-exec so that devices cannot persist if we fork/exec for update
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
::pipe(_shutdownSignalPipe);
_thread = Thread::start(this);
}
NetBSDEthernetTap::~NetBSDEthernetTap()
{
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
Thread::join(_thread);
::close(_fd);
::close(_shutdownSignalPipe[0]);
::close(_shutdownSignalPipe[1]);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
}
cpid = (long)vfork();
if (cpid == 0) {
string tmp="/dev/";
tmp+=_dev.c_str();
::execl("/bin/rm","/bin/rm",tmp.c_str(),(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
} else throw std::runtime_error("fork() failed");
}
void NetBSDEthernetTap::setEnabled(bool en)
{
_enabled = en;
}
bool NetBSDEthernetTap::enabled() const
{
return _enabled;
}
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
{
long cpid = (long)vfork();
if (cpid == 0) {
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
waitpid(cpid,&exitcode,0);
return (exitcode == 0);
}
return false; // never reached, make compiler shut up about return value
}
bool NetBSDEthernetTap::addIp(const InetAddress &ip)
{
if (!ip)
return false;
std::vector<InetAddress> allIps(ips());
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end())
return true; // IP/netmask already assigned
// Remove and reconfigure if address is the same but netmask is different
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;
}
}
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
::waitpid(cpid,&exitcode,0);
return (exitcode == 0);
}
return false;
}
bool NetBSDEthernetTap::removeIp(const InetAddress &ip)
{
if (!ip)
return false;
std::vector<InetAddress> allIps(ips());
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) {
if (___removeIp(_dev,ip))
return true;
}
return false;
}
std::vector<InetAddress> NetBSDEthernetTap::ips() const
{
struct ifaddrs *ifa = (struct ifaddrs *)0;
if (getifaddrs(&ifa))
return std::vector<InetAddress>();
std::vector<InetAddress> r;
struct ifaddrs *p = ifa;
while (p) {
if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) {
switch(p->ifa_addr->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask;
r.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.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;
}
}
p = p->ifa_next;
}
if (ifa)
freeifaddrs(ifa);
std::sort(r.begin(),r.end());
std::unique(r.begin(),r.end());
return r;
}
void NetBSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
char putBuf[4096];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
memcpy(putBuf + 14,data,len);
len += 14;
::write(_fd,putBuf,len);
}
}
std::string NetBSDEthernetTap::deviceName() const
{
return _dev;
}
void NetBSDEthernetTap::setFriendlyName(const char *friendlyName)
{
}
void NetBSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
{
std::vector<MulticastGroup> newGroups;
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
if (!getifmaddrs(&ifmap)) {
struct ifmaddrs *p = ifmap;
while (p) {
if (p->ifma_addr->sa_family == AF_LINK) {
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
}
p = p->ifma_next;
}
freeifmaddrs(ifmap);
}
std::vector<InetAddress> allIps(ips());
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
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);
}
_multicastGroups.swap(newGroups);
}
/*
bool NetBSDEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
{
std::set<MulticastGroup> newGroups;
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
if (!getifmaddrs(&ifmap)) {
struct ifmaddrs *p = ifmap;
while (p) {
if (p->ifma_addr->sa_family == AF_LINK) {
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
}
p = p->ifma_next;
}
freeifmaddrs(ifmap);
}
{
std::set<InetAddress> allIps(ips());
for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i)
newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
}
bool changed = false;
for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
if (!groups.count(*mg)) {
groups.insert(*mg);
changed = true;
}
}
for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) {
groups.erase(mg++);
changed = true;
} else ++mg;
}
return changed;
}
*/
void NetBSDEthernetTap::threadMain()
throw()
{
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
char getBuf[8194];
// Wait for a moment after startup -- wait for Network to finish
// constructing itself.
Thread::sleep(500);
FD_ZERO(&readfds);
FD_ZERO(&nullfds);
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
r = 0;
for(;;) {
FD_SET(_shutdownSignalPipe[0],&readfds);
FD_SET(_fd,&readfds);
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
break;
if (FD_ISSET(_fd,&readfds)) {
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
if (n < 0) {
if ((errno != EINTR)&&(errno != ETIMEDOUT))
break;
} else {
// Some tap drivers like to send the ethernet frame and the
// payload in two chunks, so handle that by accumulating
// data until we have at least a frame.
r += n;
if (r > 14) {
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
r = _mtu + 14;
if (_enabled) {
to.setTo(getBuf,6);
from.setTo(getBuf + 6,6);
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
// TODO: VLAN support
_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
}
r = 0;
}
}
}
}
}
} // namespace ZeroTier

View file

@ -1,76 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_NetBSDEthernetTap_HPP
#define ZT_NetBSDEthernetTap_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <stdexcept>
#include "../core/Constants.hpp"
#include "../core/MulticastGroup.hpp"
#include "../core/MAC.hpp"
#include "Thread.hpp"
#include "EthernetTap.hpp"
namespace ZeroTier {
class NetBSDEthernetTap : public EthernetTap
{
public:
NetBSDEthernetTap(
const char *homePath,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
virtual ~NetBSDEthernetTap();
virtual void setEnabled(bool en);
virtual bool enabled() const;
virtual bool addIp(const InetAddress &ip);
virtual bool removeIp(const InetAddress &ip);
virtual std::vector<InetAddress> ips() const;
virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
virtual std::string deviceName() const;
virtual void setFriendlyName(const char *friendlyName);
virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
void threadMain()
throw();
private:
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 _dev;
std::vector<MulticastGroup> _multicastGroups;
unsigned int _mtu;
unsigned int _metric;
int _fd;
int _shutdownSignalPipe[2];
volatile bool _enabled;
};
} // namespace ZeroTier
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,162 +0,0 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_WINDOWSETHERNETTAP_HPP
#define ZT_WINDOWSETHERNETTAP_HPP
#include "../core/Constants.hpp"
#ifdef __WINDOWS__
#include <stdio.h>
#include <stdlib.h>
#include <ifdef.h>
#include <string>
#include <queue>
#include <stdexcept>
#include "../core/Mutex.hpp"
#include "../core/MulticastGroup.hpp"
#include "../core/InetAddress.hpp"
#include "../osdep/Thread.hpp"
#include "EthernetTap.hpp"
namespace ZeroTier {
class WindowsEthernetTap : public EthernetTap
{
public:
/**
* Installs a new instance of the ZT tap driver
*
* @param pathToInf Path to zttap driver .inf file
* @param deviceInstanceId Buffer to fill with device instance ID on success (and if SetupDiGetDeviceInstanceIdA succeeds, which it should)
* @return Empty string on success, otherwise an error message
*/
static std::string addNewPersistentTapDevice(const char *pathToInf,std::string &deviceInstanceId);
/**
* Uninstalls all persistent tap devices that have legacy drivers
*
* @return Empty string on success, otherwise an error message
*/
static std::string destroyAllLegacyPersistentTapDevices();
/**
* Uninstalls all persistent tap devices on the system
*
* @return Empty string on success, otherwise an error message
*/
static std::string destroyAllPersistentTapDevices();
/**
* Uninstalls a specific persistent tap device by instance ID
*
* @param instanceId Device instance ID
* @return Empty string on success, otherwise an error message
*/
static std::string deletePersistentTapDevice(const char *instanceId);
/**
* Disable a persistent tap device by instance ID
*
* @param instanceId Device instance ID
* @param enabled Enable device?
* @return True if device was found and disabled
*/
static bool setPersistentTapDeviceState(const char *instanceId,bool enabled);
WindowsEthernetTap(
const char *hp,
const MAC &mac,
unsigned int mtu,
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
virtual ~WindowsEthernetTap();
virtual void setEnabled(bool en);
virtual bool enabled() const;
virtual bool addIp(const InetAddress &ip);
virtual bool removeIp(const InetAddress &ip);
virtual std::vector<InetAddress> ips() const;
virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
virtual std::string deviceName() const;
virtual void setFriendlyName(const char *friendlyName);
virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
virtual void setMtu(unsigned int mtu);
inline const NET_LUID &luid() const { return _deviceLuid; }
inline const GUID &guid() const { return _deviceGuid; }
inline const std::string &instanceId() const { return _deviceInstanceId; }
NET_IFINDEX interfaceIndex() const;
void threadMain()
throw();
bool isInitialized() const { return _initialized; };
private:
NET_IFINDEX _getDeviceIndex(); // throws on failure
std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
void _syncIps();
void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
MAC _mac;
uint64_t _nwid;
volatile unsigned int _mtu;
Thread _thread;
volatile HANDLE _tap;
HANDLE _injectSemaphore;
GUID _deviceGuid;
NET_LUID _deviceLuid;
std::string _netCfgInstanceId;
std::string _deviceInstanceId;
std::string _mySubkeyName;
std::string _friendlyName;
std::vector<InetAddress> _assignedIps; // IPs assigned with addIp
Mutex _assignedIps_m;
std::vector<MulticastGroup> _multicastGroups;
struct _InjectPending
{
unsigned int len;
char data[ZT_MAX_MTU + 32];
};
std::queue<_InjectPending> _injectPending;
Mutex _injectPending_m;
std::string _pathToHelpers;
volatile bool _run;
volatile bool _initialized;
volatile bool _enabled;
};
} // namespace ZeroTier
#endif
#endif

View file

@ -1,207 +0,0 @@
/*
this BSD licensed code is from https://github.com/freebsd/freebsd/blob/386ddae58459341ec567604707805814a2128a57/lib/libc/net/getifmaddrs.c
as in older OS X there is no getifmaddrs() and related functions is NetBSD
*/
#define NET_RT_IFMALIST 4 /* return multicast address list */
#define RTM_NEWMADDR 0xf /* mcast group membership being added to if */
/*
* Copyright (c) 2003 Bruce M. Simpson.
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
/*
__FBSDID("$FreeBSD$");
#include "namespace.h"*/
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <errno.h>
#include <ifaddrs.h>
#include <stdlib.h>
#include <string.h>
//#include "un-namespace.h"
#define SALIGN (sizeof(long) - 1)
#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
(SALIGN + 1))
#define MAX_SYSCTL_TRY 5
#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
int
getifmaddrs(struct ifmaddrs **pif)
{
int icnt = 1;
int dcnt = 0;
int ntry = 0;
size_t len;
size_t needed;
int mib[6];
int i;
char *buf;
char *data;
char *next;
char *p;
struct ifma_msghdr *ifmam;
struct ifmaddrs *ifa, *ift;
struct rt_msghdr *rtm;
struct sockaddr *sa;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0; /* protocol */
mib[3] = 0; /* wildcard address family */
mib[4] = NET_RT_IFMALIST;
mib[5] = 0; /* no flags */
do {
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
return (-1);
if ((buf = malloc(needed)) == NULL)
return (-1);
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
free(buf);
return (-1);
}
free(buf);
buf = NULL;
}
} while (buf == NULL);
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)(void *)next;
if (rtm->rtm_version != RTM_VERSION)
continue;
switch (rtm->rtm_type) {
case RTM_NEWMADDR:
ifmam = (struct ifma_msghdr *)(void *)rtm;
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
break;
icnt++;
p = (char *)(ifmam + 1);
for (i = 0; i < RTAX_MAX; i++) {
if ((RTA_MASKS & ifmam->ifmam_addrs &
(1 << i)) == 0)
continue;
sa = (struct sockaddr *)(void *)p;
len = SA_RLEN(sa);
dcnt += len;
p += len;
}
break;
}
}
data = malloc(sizeof(struct ifmaddrs) * icnt + dcnt);
if (data == NULL) {
free(buf);
return (-1);
}
ifa = (struct ifmaddrs *)(void *)data;
data += sizeof(struct ifmaddrs) * icnt;
memset(ifa, 0, sizeof(struct ifmaddrs) * icnt);
ift = ifa;
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)(void *)next;
if (rtm->rtm_version != RTM_VERSION)
continue;
switch (rtm->rtm_type) {
case RTM_NEWMADDR:
ifmam = (struct ifma_msghdr *)(void *)rtm;
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
break;
p = (char *)(ifmam + 1);
for (i = 0; i < RTAX_MAX; i++) {
if ((RTA_MASKS & ifmam->ifmam_addrs &
(1 << i)) == 0)
continue;
sa = (struct sockaddr *)(void *)p;
len = SA_RLEN(sa);
switch (i) {
case RTAX_GATEWAY:
ift->ifma_lladdr =
(struct sockaddr *)(void *)data;
memcpy(data, p, len);
data += len;
break;
case RTAX_IFP:
ift->ifma_name =
(struct sockaddr *)(void *)data;
memcpy(data, p, len);
data += len;
break;
case RTAX_IFA:
ift->ifma_addr =
(struct sockaddr *)(void *)data;
memcpy(data, p, len);
data += len;
break;
default:
data += len;
break;
}
p += len;
}
ift->ifma_next = ift + 1;
ift = ift->ifma_next;
break;
}
}
free(buf);
if (ift > ifa) {
ift--;
ift->ifma_next = NULL;
*pif = ifa;
} else {
*pif = NULL;
free(ifa);
}
return (0);
}
void
freeifmaddrs(struct ifmaddrs *ifmp)
{
free(ifmp);
}

View file

@ -1,75 +0,0 @@
/*
this BSD code is from https://github.com/freebsd/freebsd/blob/386ddae58459341ec567604707805814a2128a57/include/ifaddrs.h
as in older OS X there is no getifmaddrs() and related functions is NetBSD
*/
/* $FreeBSD$ */
/*
* Copyright (c) 1995, 1999
* Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
*/
#ifndef _freebsd_getifmaddrs.h_
#define _freebsd_getifmaddrs.h_
/*
* This may have been defined in <net/if.h>. Note that if <net/if.h> is
* to be included it must be included before this header file.
*/
#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
#endif
struct ifmaddrs {
struct ifmaddrs *ifma_next;
struct sockaddr *ifma_name;
struct sockaddr *ifma_addr;
struct sockaddr *ifma_lladdr;
};
#include <sys/cdefs.h>
/*
* Message format for use in obtaining information about multicast addresses
* from the routing socket.
*/
struct ifma_msghdr {
int ifmam_msglen; /* to skip over non-understood messages */
int ifmam_version; /* future binary compatibility */
int ifmam_type; /* message type */
int ifmam_addrs; /* like rtm_addrs */
int ifmam_flags; /* value of ifa_flags */
int ifmam_index; /* index for associated ifp */
};
extern int getifaddrs(struct ifaddrs **);
extern void freeifaddrs(struct ifaddrs *);
extern int getifmaddrs(struct ifmaddrs **);
extern void freeifmaddrs(struct ifmaddrs *);
#include "freebsd_getifmaddrs.c"
#endif

View file

@ -1,3 +1,12 @@
/* Fix for an issue with this structure not being present on MacOS */
#ifdef __APPLE__
struct prf_ra {
unsigned char onlink : 1;
unsigned char autonomous : 1;
unsigned char reserved : 6;
} prf_ra;
#endif
#include "../core/Constants.hpp"
#include "../core/Mutex.hpp"
#include "../core/Containers.hpp"
@ -104,4 +113,7 @@ const char *platformDefaultHomePath()
int64_t msSinceEpoch()
{ return ZeroTier::OSUtils::now(); }
void lockDownFile(const char *path, int isDir)
{ ZeroTier::OSUtils::lockDownFile(path, isDir != 0); }
}

View file

@ -5,11 +5,15 @@
#ifdef __APPLE__
#include <unistd.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <errno.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/cdefs.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@ -53,6 +57,7 @@ extern "C" {
extern const char *platformDefaultHomePath();
extern int64_t msSinceEpoch();
extern void lockDownFile(const char *path, int isDir);
#ifdef __cplusplus
}

View file

@ -13,7 +13,6 @@
use std::cell::Cell;
use std::collections::hash_map::HashMap;
use std::ffi::CStr;
use std::intrinsics::copy_nonoverlapping;
use std::mem::{MaybeUninit, transmute};
use std::os::raw::{c_int, c_uint, c_ulong, c_void};
@ -28,7 +27,8 @@ use serde::{Deserialize, Serialize};
use crate::*;
use crate::capi as ztcore;
const NODE_BACKGROUND_MAX_DELAY: i64 = 500;
/// Maximum delay between calls to run_background_tasks()
pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: u64 = 250;
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)]
pub enum Event {
@ -77,10 +77,10 @@ pub trait NodeEventHandler<N: 'static> {
fn event(&self, event: Event, event_data: &[u8]);
/// Called to store an object into the object store.
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]);
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()>;
/// Called to retrieve an object from the object store.
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option<Box<[u8]>>;
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result<Vec<u8>>;
/// Called to send a packet over the physical network (virtual -> physical).
fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32;
@ -95,11 +95,10 @@ pub trait NodeEventHandler<N: 'static> {
/// An instance of the ZeroTier core.
/// This is templated on the actual implementation of NodeEventHandler for performance reasons,
/// as it avoids an extra indirect function call.
pub struct Node<T: NodeEventHandler<N> + 'static, N: 'static> {
event_handler: Arc<T>,
capi: Cell<*mut ztcore::ZT_Node>,
background_thread: Cell<Option<std::thread::JoinHandle<()>>>,
background_thread_run: Arc<AtomicBool>,
#[allow(non_snake_case)]
pub struct Node<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static> {
event_handler: T,
capi: *mut ztcore::ZT_Node,
now: PortableAtomicI64,
networks_by_id: Mutex<HashMap<u64, *mut Arc<N>>> // pointer to an Arc<> is a raw value created from Box<Arc<N>>
}
@ -116,7 +115,7 @@ macro_rules! node_from_raw_ptr {
}
}
extern "C" fn zt_virtual_network_config_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_virtual_network_config_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -139,7 +138,7 @@ extern "C" fn zt_virtual_network_config_function<T: NodeEventHandler<N> + 'stati
}
}
extern "C" fn zt_virtual_network_frame_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_virtual_network_frame_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -165,7 +164,7 @@ extern "C" fn zt_virtual_network_frame_function<T: NodeEventHandler<N> + 'static
}
}
extern "C" fn zt_event_callback<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_event_callback<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -186,7 +185,7 @@ extern "C" fn zt_event_callback<T: NodeEventHandler<N> + 'static, N: 'static>(
}
}
extern "C" fn zt_state_put_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_state_put_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -202,11 +201,11 @@ extern "C" fn zt_state_put_function<T: NodeEventHandler<N> + 'static, N: 'static
let n = node_from_raw_ptr!(uptr);
let obj_id2 = unsafe { &*slice_from_raw_parts(obj_id, obj_id_len as usize) };
let obj_data2 = unsafe { &*slice_from_raw_parts(obj_data.cast::<u8>(), obj_data_len as usize) };
n.event_handler.state_put(obj_type2, obj_id2, obj_data2);
let _ = n.event_handler.state_put(obj_type2, obj_id2, obj_data2);
}
}
extern "C" fn zt_state_get_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_state_get_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -230,7 +229,7 @@ extern "C" fn zt_state_get_function<T: NodeEventHandler<N> + 'static, N: 'static
let n = node_from_raw_ptr!(uptr);
let obj_id2 = unsafe { &*slice_from_raw_parts(obj_id, obj_id_len as usize) };
let obj_data_result = n.event_handler.state_get(obj_type2, obj_id2);
if obj_data_result.is_some() {
if obj_data_result.is_ok() {
let obj_data_result = obj_data_result.unwrap();
if obj_data_result.len() > 0 {
unsafe {
@ -248,7 +247,7 @@ extern "C" fn zt_state_get_function<T: NodeEventHandler<N> + 'static, N: 'static
return -1;
}
extern "C" fn zt_wire_packet_send_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_wire_packet_send_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -262,7 +261,7 @@ extern "C" fn zt_wire_packet_send_function<T: NodeEventHandler<N> + 'static, N:
return n.event_handler.wire_packet_send(local_socket, InetAddress::transmute_capi(unsafe { &*sock_addr }), unsafe { &*slice_from_raw_parts(data.cast::<u8>(), data_size as usize) }, packet_ttl as u32) as c_int;
}
extern "C" fn zt_path_check_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_path_check_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -279,7 +278,7 @@ extern "C" fn zt_path_check_function<T: NodeEventHandler<N> + 'static, N: 'stati
return 0;
}
extern "C" fn zt_path_lookup_function<T: NodeEventHandler<N> + 'static, N: 'static>(
extern "C" fn zt_path_lookup_function<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static>(
_: *mut ztcore::ZT_Node,
uptr: *mut c_void,
_: *mut c_void,
@ -318,19 +317,17 @@ extern "C" fn zt_path_lookup_function<T: NodeEventHandler<N> + 'static, N: 'stat
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
impl<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static> Node<T, N> {
/// Create a new Node with a given event handler.
pub fn new(event_handler: Arc<T>) -> Result<Arc<Node<T, N>>, ResultCode> {
pub fn new(event_handler: T) -> Result<Node<T, N>, ResultCode> {
let now = now();
let n = Arc::new(Node {
let mut n = Node {
event_handler: event_handler.clone(),
capi: Cell::new(null_mut()),
background_thread: Cell::new(None),
background_thread_run: Arc::new(AtomicBool::new(true)),
capi: null_mut(),
now: PortableAtomicI64::new(now),
networks_by_id: Mutex::new(HashMap::new())
});
};
let mut capi: *mut ztcore::ZT_Node = null_mut();
unsafe {
@ -344,55 +341,47 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
pathCheckFunction: transmute(zt_path_check_function::<T, N> as *const ()),
pathLookupFunction: transmute(zt_path_lookup_function::<T, N> as *const ()),
};
let rc = ztcore::ZT_Node_new(&mut capi as *mut *mut ztcore::ZT_Node, transmute(Arc::as_ptr(&n)), null_mut(), &callbacks as *const ztcore::ZT_Node_Callbacks, now);
let rc = ztcore::ZT_Node_new(&mut capi as *mut *mut ztcore::ZT_Node, transmute(&n as *const Node<T, N>), null_mut(), &callbacks as *const ztcore::ZT_Node_Callbacks, now);
if rc != 0 {
return Err(ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::FatalErrorInternal));
} else if capi.is_null() {
return Err(ResultCode::FatalErrorInternal);
}
}
n.capi.replace(capi);
let wn = Arc::downgrade(&n);
let run = n.background_thread_run.clone();
n.background_thread.replace(Some(std::thread::Builder::new().stack_size(RECOMMENDED_THREAD_STACK_SIZE).spawn(move || {
let mut loop_delay = Duration::from_millis(NODE_BACKGROUND_MAX_DELAY as u64);
while run.load(Ordering::Relaxed) {
std::thread::park_timeout(loop_delay);
if run.load(Ordering::Relaxed) {
let nn = wn.upgrade();
if nn.is_some() {
loop_delay = Duration::from_millis(nn.unwrap().process_background_tasks() as u64);
} else {
break;
}
} else {
break;
}
}
}).unwrap()));
n.capi = capi;
Ok(n)
}
/// This is called periodically from the background service thread. It's not called from anywhere else.
fn process_background_tasks(&self) -> i64 {
/// Perform periodic background tasks.
/// The first call should happen no more than NODE_BACKGROUND_TASKS_MAX_INTERVAL milliseconds
/// since the node was created, and after this runs it returns the amount of time the caller
/// should wait before calling it again.
pub fn process_background_tasks(&self) -> u64 {
let current_time = now();
self.now.set(current_time);
let mut next_task_deadline: i64 = current_time;
unsafe {
ztcore::ZT_Node_processBackgroundTasks(self.capi.get(), null_mut(), current_time, &mut next_task_deadline as *mut i64);
ztcore::ZT_Node_processBackgroundTasks(self.capi, null_mut(), current_time, (&mut next_task_deadline as *mut i64).cast());
}
let mut next_delay = next_task_deadline - current_time;
if next_delay < 10 {
next_delay = 10;
} else if next_delay > NODE_BACKGROUND_MAX_DELAY {
next_delay = NODE_BACKGROUND_MAX_DELAY;
if next_delay < 1 {
next_delay = 1;
} else if next_delay > NODE_BACKGROUND_TASKS_MAX_INTERVAL as i64 {
next_delay = NODE_BACKGROUND_TASKS_MAX_INTERVAL as i64;
}
next_delay as u64
}
fn delete_network_uptr(&self, nwid: u64) {
let nptr = self.networks_by_id.lock().unwrap().remove(&nwid).unwrap_or(null_mut());
if !nptr.is_null() {
unsafe {
Box::from_raw(nptr);
}
}
next_delay
}
pub fn join(&self, nwid: NetworkId, controller_fingerprint: Option<Fingerprint>, network_obj: &Arc<N>) -> ResultCode {
@ -409,33 +398,24 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
let nptr = Box::into_raw(Box::new(network_obj.clone()));
self.networks_by_id.lock().as_deref_mut().unwrap().insert(nwid.0, nptr);
let rc = unsafe { ztcore::ZT_Node_join(self.capi.get(), nwid.0, cfpp, nptr.cast::<c_void>(), null_mut()) };
let rc = unsafe { ztcore::ZT_Node_join(self.capi, nwid.0, cfpp, nptr.cast::<c_void>(), null_mut()) };
if rc != ztcore::ZT_ResultCode_ZT_RESULT_OK {
self.delete_network_uptr(nwid.0);
}
return ResultCode::from_i32(rc as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
}
fn delete_network_uptr(&self, nwid: u64) {
let nptr = self.networks_by_id.lock().as_deref_mut().unwrap().remove(&nwid).unwrap_or(null_mut());
if !nptr.is_null() {
unsafe {
Box::from_raw(nptr);
}
}
}
pub fn leave(&self, nwid: NetworkId) -> ResultCode {
self.delete_network_uptr(nwid.0);
unsafe {
return ResultCode::from_i32(ztcore::ZT_Node_leave(self.capi.get(), nwid.0, null_mut(), null_mut()) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
return ResultCode::from_i32(ztcore::ZT_Node_leave(self.capi, nwid.0, null_mut(), null_mut()) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
}
}
#[inline(always)]
pub fn address(&self) -> Address {
unsafe {
return Address(ztcore::ZT_Node_address(self.capi.get()) as u64);
return Address(ztcore::ZT_Node_address(self.capi) as u64);
}
}
@ -443,7 +423,7 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
pub fn process_wire_packet(&self, local_socket: i64, remote_address: &InetAddress, data: Buffer) -> ResultCode {
let current_time = self.now.get();
let mut next_task_deadline: i64 = current_time;
let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processWirePacket(self.capi.get(), null_mut(), current_time, local_socket, remote_address.as_capi_ptr(), data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_task_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) };
let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processWirePacket(self.capi, null_mut(), current_time, local_socket, remote_address.as_capi_ptr(), data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_task_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) };
std::mem::forget(data); // prevent Buffer from being returned to ZT core twice, see comment in drop() in buffer.rs
rc
}
@ -452,7 +432,7 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
pub fn process_virtual_network_frame(&self, nwid: &NetworkId, source_mac: &MAC, dest_mac: &MAC, ethertype: u16, vlan_id: u16, data: Buffer) -> ResultCode {
let current_time = self.now.get();
let mut next_tick_deadline: i64 = current_time;
let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processVirtualNetworkFrame(self.capi.get(), null_mut(), current_time, nwid.0, source_mac.0, dest_mac.0, ethertype as c_uint, vlan_id as c_uint, data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_tick_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) };
let rc = unsafe { ResultCode::from_i32(ztcore::ZT_Node_processVirtualNetworkFrame(self.capi, null_mut(), current_time, nwid.0, source_mac.0, dest_mac.0, ethertype as c_uint, vlan_id as c_uint, data.zt_core_buf as *const c_void, data.data_size as u32, 1, &mut next_tick_deadline as *mut i64) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal) };
std::mem::forget(data); // prevent Buffer from being returned to ZT core twice, see comment in drop() in buffer.rs
rc
}
@ -460,14 +440,14 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
#[inline(always)]
pub fn multicast_subscribe(&self, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
unsafe {
return ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.capi.get(), null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
return ResultCode::from_i32(ztcore::ZT_Node_multicastSubscribe(self.capi, null_mut(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
}
}
#[inline(always)]
pub fn multicast_unsubscribe(&self, nwid: &NetworkId, multicast_group: &MAC, multicast_adi: u32) -> ResultCode {
unsafe {
return ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.capi.get(), nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
return ResultCode::from_i32(ztcore::ZT_Node_multicastUnsubscribe(self.capi, nwid.0, multicast_group.0, multicast_adi as c_ulong) as i32).unwrap_or(ResultCode::ErrorInternalNonFatal);
}
}
@ -482,13 +462,13 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
/// to exist the Identity becomes invalid. Use clone() on it to get a copy.
#[inline(always)]
pub(crate) unsafe fn identity_fast(&self) -> Identity {
Identity::new_from_capi(ztcore::ZT_Node_identity(self.capi.get()), false)
Identity::new_from_capi(ztcore::ZT_Node_identity(self.capi), false)
}
pub fn status(&self) -> NodeStatus {
let mut ns: MaybeUninit<ztcore::ZT_NodeStatus> = MaybeUninit::zeroed();
unsafe {
ztcore::ZT_Node_status(self.capi.get(), ns.as_mut_ptr());
ztcore::ZT_Node_status(self.capi, ns.as_mut_ptr());
let ns = ns.assume_init();
if ns.identity.is_null() {
panic!("ZT_Node_status() returned null identity");
@ -496,8 +476,8 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
return NodeStatus {
address: Address(ns.address),
identity: Identity::new_from_capi(&*ns.identity, false).clone(),
public_identity: String::from(CStr::from_ptr(ns.publicIdentity).to_str().unwrap()),
secret_identity: String::from(CStr::from_ptr(ns.secretIdentity).to_str().unwrap()),
public_identity: cstr_to_string(ns.publicIdentity, -1),
secret_identity: cstr_to_string(ns.secretIdentity, -1),
online: ns.online != 0,
};
}
@ -506,7 +486,7 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
pub fn peers(&self) -> Vec<Peer> {
let mut p: Vec<Peer> = Vec::new();
unsafe {
let pl = ztcore::ZT_Node_peers(self.capi.get());
let pl = ztcore::ZT_Node_peers(self.capi);
if !pl.is_null() {
let peer_count = (*pl).peerCount as usize;
p.reserve(peer_count);
@ -522,7 +502,7 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
pub fn networks(&self) -> Vec<VirtualNetworkConfig> {
let mut n: Vec<VirtualNetworkConfig> = Vec::new();
unsafe {
let nl = ztcore::ZT_Node_networks(self.capi.get());
let nl = ztcore::ZT_Node_networks(self.capi);
if !nl.is_null() {
let net_count = (*nl).networkCount as usize;
n.reserve(net_count);
@ -538,7 +518,7 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
pub fn certificates(&self) -> Vec<(Certificate, u32)> {
let mut c: Vec<(Certificate, u32)> = Vec::new();
unsafe {
let cl = ztcore::ZT_Node_listCertificates(self.capi.get());
let cl = ztcore::ZT_Node_listCertificates(self.capi);
if !cl.is_null() {
let cert_count = (*cl).certCount as usize;
c.reserve(cert_count);
@ -552,20 +532,12 @@ impl<T: NodeEventHandler<N> + 'static, N: 'static> Node<T, N> {
}
}
unsafe impl<T: NodeEventHandler<N> + 'static, N: 'static> Sync for Node<T, N> {}
unsafe impl<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static> Sync for Node<T, N> {}
unsafe impl<T: NodeEventHandler<N> + 'static, N: 'static> Send for Node<T, N> {}
unsafe impl<T: NodeEventHandler<N> + Sync + Send + Clone + 'static, N: 'static> Send for Node<T, N> {}
impl<T: NodeEventHandler<N> + 'static,N: 'static> Drop for Node<T, N> {
impl<T: NodeEventHandler<N> + Sync + Send + Clone + 'static,N: 'static> Drop for Node<T, N> {
fn drop(&mut self) {
self.background_thread_run.store(false, Ordering::Relaxed);
let bt = self.background_thread.replace(None);
if bt.is_some() {
let bt = bt.unwrap();
bt.thread().unpark();
let _ = bt.join();
}
// Manually take care of the unboxed Boxes in networks_by_id
let mut nwids: Vec<u64> = Vec::new();
for n in self.networks_by_id.lock().unwrap().iter() {
@ -576,7 +548,7 @@ impl<T: NodeEventHandler<N> + 'static,N: 'static> Drop for Node<T, N> {
}
unsafe {
ztcore::ZT_Node_delete(self.capi.get(), null_mut());
ztcore::ZT_Node_delete(self.capi, null_mut());
}
}
}

View file

@ -11,6 +11,7 @@
*/
/****/
use std::cell::Cell;
use std::collections::BTreeMap;
use std::net::IpAddr;
use std::rc::Rc;
@ -24,17 +25,32 @@ use warp::{Filter, Rejection, Reply};
use warp::http::{HeaderMap, Method, StatusCode};
use warp::hyper::body::Bytes;
use zerotier_core::*;
use zerotier_core::{Buffer, Address, IpScope, Node, NodeEventHandler, NetworkId, VirtualNetworkConfigOperation, VirtualNetworkConfig, StateObjectType, MAC, Event, InetAddress, InetAddressFamily, Identity};
use crate::fastudpsocket::*;
use crate::getifaddrs;
use crate::localconfig::*;
use crate::log::Log;
use crate::network::Network;
use crate::store::Store;
// Check local addresses and bindings every (this) milliseconds.
const BINDING_CHECK_INTERVAL: i64 = 5000;
struct Service {
local_config: Mutex<LocalConfig>,
run: AtomicBool,
local_config: Mutex<Arc<LocalConfig>>,
run: Arc<AtomicBool>,
store: Arc<Store>,
}
impl Clone for Service {
fn clone(&self) -> Self {
Service {
local_config: Mutex::new(self.local_config.lock().unwrap().clone()),
run: self.run.clone(),
store: self.store.clone(),
}
}
}
impl NodeEventHandler<Network> for Service {
@ -46,13 +62,24 @@ impl NodeEventHandler<Network> for Service {
}
fn event(&self, event: Event, event_data: &[u8]) {
match event {
Event::Up => {},
Event::Down => {},
Event::Online => {},
Event::Offline => {},
Event::Trace => {},
Event::UserMessage => {},
}
}
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) {
#[inline(always)]
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> {
self.store.store_object(&obj_type, obj_id, obj_data)
}
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option<Box<[u8]>> {
None
#[inline(always)]
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result<Vec<u8>> {
self.store.load_object(&obj_type, obj_id)
}
#[inline(always)]
@ -65,7 +92,18 @@ impl NodeEventHandler<Network> for Service {
}
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress> {
None
let lc = self.get_local_config();
let vc = lc.virtual_.get(&address);
vc.map_or(None, |c: &LocalConfigVirtualConfig| {
if c.try_.is_empty() {
None
} else {
let t = c.try_.get((zerotier_core::random() as usize) % c.try_.len());
t.map_or(None, |v: &InetAddress| {
Some(v.clone())
})
}
})
}
}
@ -84,9 +122,20 @@ impl Service {
fn web_api_peer(&self, peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes) -> Box<dyn Reply> {
Box::new(warp::http::StatusCode::BAD_REQUEST)
}
#[inline(always)]
fn get_local_config(&self) -> Arc<LocalConfig> {
self.local_config.lock().unwrap().clone()
}
#[inline(always)]
fn set_local_config(&self, new_lc: &Arc<LocalConfig>) {
let mut lc = self.local_config.lock().unwrap();
*lc = new_lc.clone();
}
}
pub(crate) fn run() -> i32 {
pub(crate) fn run(store: &Arc<Store>) -> i32 {
let mut process_exit_value: i32 = 0;
let tokio_rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
@ -94,36 +143,49 @@ pub(crate) fn run() -> i32 {
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
let (mut interrupt_tx, mut interrupt_rx) = futures::channel::mpsc::channel::<u8>(2);
let service: Arc<Service> = Arc::new(Service {
local_config: Mutex::new(LocalConfig::default()),
run: AtomicBool::new(true),
});
let service = Service {
local_config: Mutex::new(Arc::new(LocalConfig::default())),
run: Arc::new(AtomicBool::new(true)),
store: store.clone(),
};
let node = Node::new(service.clone());
if node.is_err() {
// TODO: log and handle error
return;
}
let node = Arc::new(node.ok().unwrap());
let mut primary_port_bind_failure = false;
let mut last_checked_bindings: i64 = 0;
let mut loop_delay = zerotier_core::NODE_BACKGROUND_TASKS_MAX_INTERVAL;
loop {
let current_local_config_settings = service.local_config.lock().unwrap().settings.clone();
let mut current_local_config = service.get_local_config();
let (mut shutdown_tx, mut shutdown_rx) = futures::channel::oneshot::channel();
let s0 = service.clone();
let s1 = service.clone();
let s2 = service.clone();
let warp_server = warp::serve(warp::any().and(warp::path::end().map(|| { warp::reply::with_status("404", warp::hyper::StatusCode::NOT_FOUND) })
.or(warp::path("status").and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes())
.map(move |method: Method, headers: HeaderMap, post_data: Bytes| { s0.web_api_status(method, headers, post_data) }))
.or(warp::path!("network" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes())
.map(move |network_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s1.web_api_network(network_str, method, headers, post_data) }))
.or(warp::path!("peer" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes())
.map(move |peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s2.web_api_peer(peer_str, method, headers, post_data) }))
)).try_bind_with_graceful_shutdown((IpAddr::from([127_u8, 0_u8, 0_u8, 1_u8]), current_local_config_settings.primary_port), async { let _ = shutdown_rx.await; });
let warp_server;
{
let s0 = service.clone();
let s1 = service.clone();
let s2 = service.clone();
warp_server = warp::serve(warp::any().and(warp::path::end().map(|| { warp::reply::with_status("404", warp::hyper::StatusCode::NOT_FOUND) })
.or(warp::path("status").and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes())
.map(move |method: Method, headers: HeaderMap, post_data: Bytes| { s0.web_api_status(method, headers, post_data) }))
.or(warp::path!("network" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes())
.map(move |network_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s1.web_api_network(network_str, method, headers, post_data) }))
.or(warp::path!("peer" / String).and(warp::method()).and(warp::header::headers_cloned()).and(warp::body::bytes())
.map(move |peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes| { s2.web_api_peer(peer_str, method, headers, post_data) }))
)).try_bind_with_graceful_shutdown((IpAddr::from([127_u8, 0_u8, 0_u8, 1_u8]), current_local_config.settings.primary_port), async { let _ = shutdown_rx.await; });
}
if warp_server.is_err() {
primary_port_bind_failure = true;
break;
}
let warp_server = tokio_rt.spawn(warp_server.unwrap().1);
let mut loop_delay = 10;
loop {
tokio::select! {
_ = tokio::time::sleep(Duration::from_secs(loop_delay)) => {},
_ = tokio::time::sleep(Duration::from_millis(loop_delay)) => {},
_ = interrupt_rx.next() => {},
_ = tokio::signal::ctrl_c() => {
// TODO: log CTRL+C received
@ -133,64 +195,73 @@ pub(crate) fn run() -> i32 {
}
}
let mut system_addrs: BTreeMap<InetAddress, String> = BTreeMap::new();
getifaddrs::for_each_address(|addr: &InetAddress, dev: &str| {
match addr.ip_scope() {
IpScope::Global | IpScope::Private | IpScope::PseudoPrivate | IpScope::Shared => {
if !current_local_config_settings.is_interface_blacklisted(dev) {
let mut a = addr.clone();
a.set_port(current_local_config_settings.primary_port);
system_addrs.insert(a, String::from(dev));
if current_local_config_settings.secondary_port.is_some() {
loop_delay = node.process_background_tasks();
let now = zerotier_core::now();
if (now - last_checked_bindings) >= BINDING_CHECK_INTERVAL {
last_checked_bindings = now;
let mut system_addrs: BTreeMap<InetAddress, String> = BTreeMap::new();
getifaddrs::for_each_address(|addr: &InetAddress, dev: &str| {
match addr.ip_scope() {
IpScope::Global | IpScope::Private | IpScope::PseudoPrivate | IpScope::Shared => {
if !current_local_config.settings.is_interface_blacklisted(dev) {
let mut a = addr.clone();
a.set_port(current_local_config_settings.secondary_port.unwrap());
a.set_port(current_local_config.settings.primary_port);
system_addrs.insert(a, String::from(dev));
if current_local_config.settings.secondary_port.is_some() {
let mut a = addr.clone();
a.set_port(current_local_config.settings.secondary_port.unwrap());
system_addrs.insert(a, String::from(dev));
}
}
}
},
_ => {}
}
});
},
_ => {}
}
});
let mut udp_sockets_to_close: Vec<InetAddress> = Vec::new();
for sock in udp_sockets.iter() {
if !system_addrs.contains_key(sock.0) {
udp_sockets_to_close.push(sock.0.clone());
}
}
for k in udp_sockets_to_close.iter() {
udp_sockets.remove(k);
}
for addr in system_addrs.iter() {
if !udp_sockets.contains_key(addr.0) {
let s = FastUDPSocket::new(addr.1.as_str(), addr.0, |raw_socket: &FastUDPRawOsSocket, from_address: &InetAddress, data: Buffer| {
// TODO: incoming packet handler
});
if s.is_ok() {
udp_sockets.insert(addr.0.clone(), s.unwrap());
let mut udp_sockets_to_close: Vec<InetAddress> = Vec::new();
for sock in udp_sockets.iter() {
if !system_addrs.contains_key(sock.0) {
udp_sockets_to_close.push(sock.0.clone());
}
}
}
for k in udp_sockets_to_close.iter() {
udp_sockets.remove(k);
}
primary_port_bind_failure = true;
for s in udp_sockets.iter() {
if s.0.port() == current_local_config_settings.primary_port {
primary_port_bind_failure = false;
for addr in system_addrs.iter() {
if !udp_sockets.contains_key(addr.0) {
let s = FastUDPSocket::new(addr.1.as_str(), addr.0, |raw_socket: &FastUDPRawOsSocket, from_address: &InetAddress, data: Buffer| {
// TODO: incoming packet handler
});
if s.is_ok() {
udp_sockets.insert(addr.0.clone(), s.unwrap());
}
}
}
primary_port_bind_failure = true;
for s in udp_sockets.iter() {
if s.0.port() == current_local_config.settings.primary_port {
primary_port_bind_failure = false;
break;
}
}
if primary_port_bind_failure {
break;
}
}
if primary_port_bind_failure {
break;
}
if !service.run.load(Ordering::Relaxed) || current_local_config_settings.primary_port != service.local_config.lock().unwrap().settings.primary_port {
let next_local_config = service.get_local_config();
if !service.run.load(Ordering::Relaxed) || current_local_config.settings.primary_port != next_local_config.settings.primary_port {
let _ = shutdown_tx.send(());
break;
}
current_local_config = next_local_config;
}
let _ = warp_server.unwrap().1.await;
let _ = warp_server.await;
if !service.run.load(Ordering::Relaxed) {
break;
@ -201,7 +272,7 @@ pub(crate) fn run() -> i32 {
}
if primary_port_bind_failure {
let local_config = service.local_config.lock().unwrap();
let local_config = service.get_local_config();
if local_config.settings.auto_port_search {
// TODO: port hunting if enabled
}

View file

@ -161,6 +161,19 @@ fn fast_udp_socket_close(socket: &FastUDPRawOsSocket) {
}
}
#[inline(always)]
pub(crate) fn fast_udp_socket_to_i64(socket: &FastUDPRawOsSocket) -> i64 {
(*socket) as i64
}
#[inline(always)]
pub(crate) fn fast_udp_socket_from_i64(socket: i64) -> Option<FastUDPRawOsSocket> {
if socket >= 0 {
return Some(socket as FastUDPRawOsSocket);
}
None
}
/// Send to a raw UDP socket with optional packet TTL.
/// If the packet_ttl option is <=0, packet is sent with the default TTL. TTL setting is only used
/// in ZeroTier right now to do escalating TTL probes for IPv4 NAT traversal.
@ -303,58 +316,42 @@ impl Drop for FastUDPSocket {
}
}
/*
#[cfg(test)]
mod tests {
use crate::fastudpsocket::*;
use zerotier_core::{InetAddress, Buffer};
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
#[allow(dead_code)]
struct TestPacketHandler {
cnt: AtomicU32,
side: &'static str
}
impl FastUDPSocketPacketHandler for TestPacketHandler {
#[allow(unused)]
fn incoming_udp_packet(&self, raw_socket: &FastUDPRawOsSocket, from_adddress: &InetAddress, data: Buffer) {
self.cnt.fetch_add(1, Ordering::Relaxed);
//println!("{}: {} bytes from {} (socket: {})", self.side, data.len(), from_adddress.to_string().as_str(), *raw_socket);
}
}
#[test]
fn test_udp_bind_and_transfer() {
{
let ba1 = InetAddress::new_from_string("127.0.0.1/23333");
let ba0 = InetAddress::new_from_string("127.0.0.1/23333");
assert!(ba0.is_some());
let ba0 = ba0.unwrap();
let cnt0 = Arc::new(AtomicU32::new(0));
let cnt0c = cnt0.clone();
let s0 = FastUDPSocket::new("", &ba0, move |sock: &FastUDPRawOsSocket, _: &InetAddress, data: Buffer| {
cnt0c.fetch_add(1, Ordering::Relaxed);
});
assert!(s0.is_ok());
let s0 = s0.unwrap();
let ba1 = InetAddress::new_from_string("127.0.0.1/23334");
assert!(ba1.is_some());
let ba1 = ba1.unwrap();
let h1: Arc<TestPacketHandler> = Arc::new(TestPacketHandler {
cnt: AtomicU32::new(0),
side: "Alice",
let cnt1 = Arc::new(AtomicU32::new(0));
let cnt1c = cnt1.clone();
let s1 = FastUDPSocket::new("", &ba1, move |sock: &FastUDPRawOsSocket, _: &InetAddress, data: Buffer| {
cnt1c.fetch_add(1, Ordering::Relaxed);
});
let s1 = FastUDPSocket::new("lo0", &ba1, &h1);
assert!(s1.is_ok());
let s1 = s1.ok().unwrap();
let ba2 = InetAddress::new_from_string("127.0.0.1/23334");
assert!(ba2.is_some());
let ba2 = ba2.unwrap();
let h2: Arc<TestPacketHandler> = Arc::new(TestPacketHandler {
cnt: AtomicU32::new(0),
side: "Bob",
});
let s2 = FastUDPSocket::new("lo0", &ba2, &h2);
assert!(s2.is_ok());
let s2 = s2.ok().unwrap();
let s1 = s1.unwrap();
let data_bytes = [0_u8; 1024];
loop {
s1.send(&ba2, data_bytes.as_ptr(), data_bytes.len(), 0);
s2.send(&ba1, data_bytes.as_ptr(), data_bytes.len(), 0);
if h1.cnt.load(Ordering::Relaxed) > 10000 && h2.cnt.load(Ordering::Relaxed) > 10000 {
s0.send(&ba1, data_bytes.as_ptr(), data_bytes.len(), 0);
s1.send(&ba0, data_bytes.as_ptr(), data_bytes.len(), 0);
if cnt0.load(Ordering::Relaxed) > 10000 && cnt1.load(Ordering::Relaxed) > 10000 {
break;
}
}
@ -362,4 +359,3 @@ mod tests {
//println!("FastUDPSocket shutdown successful");
}
}
*/

View file

@ -11,11 +11,14 @@
*/
/****/
use zerotier_core::InetAddress;
use std::ffi::CStr;
use std::ptr::{null_mut, copy_nonoverlapping};
use std::mem::size_of;
use std::ptr::{copy_nonoverlapping, null_mut};
use num_traits::cast::AsPrimitive;
use zerotier_core::InetAddress;
use crate::osdep as osdep;
#[inline(always)]

View file

@ -15,19 +15,24 @@ use std::cell::Cell;
use std::fmt::Display;
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom, Write};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::atomic::{AtomicUsize, Ordering, AtomicBool};
use std::sync::Mutex;
use chrono::Datelike;
use zerotier_core::PortableAtomicI64;
struct LogIntl {
file: Option<File>,
cur_size: u64,
max_size: usize,
enabled: bool
}
pub(crate) struct Log {
prefix: String,
path: String,
file: Mutex<Cell<Option<File>>>,
cur_size: PortableAtomicI64,
max_size: AtomicUsize,
intl: Mutex<LogIntl>
}
impl Log {
@ -41,53 +46,81 @@ impl Log {
Log{
prefix: p,
path: String::from(path),
file: Mutex::new(Cell::new(None)),
cur_size: PortableAtomicI64::new(0),
max_size: AtomicUsize::new(if max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { max_size }),
intl: Mutex::new(LogIntl {
file: None,
cur_size: 0,
max_size: if max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { max_size },
enabled: true,
}),
}
}
pub fn set_max_size(&self, new_max_size: usize) {
self.max_size.store(if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size },Ordering::Relaxed);
self.intl.lock().unwrap().max_size = if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size };
}
pub fn log<S: Into<String>>(&self, s: S) {
let mut fc = self.file.lock().unwrap();
pub fn set_enabled(&self, enabled: bool) {
self.intl.lock().unwrap().enabled = enabled;
}
pub fn log<S: AsRef<str>>(&self, s: S) {
let mut l = self.intl.lock().unwrap();
if l.enabled {
if l.file.is_none() {
let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str());
if f.is_err() {
return;
}
let mut f = f.unwrap();
let eof = f.seek(SeekFrom::End(0));
if eof.is_err() {
return;
}
l.cur_size = eof.unwrap();
l.file = Some(f);
}
if l.max_size > 0 && l.cur_size > l.max_size as u64 {
l.file = None;
l.cur_size = 0;
let max_size = self.max_size.load(Ordering::Relaxed);
if max_size > 0 && fc.get_mut().is_some() {
if self.cur_size.get() >= max_size as i64 {
fc.replace(None); // close and dispose of old File
let mut old_path = self.path.clone();
old_path.push_str(".old");
let _ = std::fs::remove_file(old_path.as_str());
let _ = std::fs::rename(self.path.as_str(), old_path.as_str());
let _ = std::fs::remove_file(self.path.as_str()); // should fail
self.cur_size.set(0);
}
}
if fc.get_mut().is_none() {
let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str());
if f.is_err() {
return;
let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str());
if f.is_err() {
return;
}
l.file = Some(f.unwrap());
}
let mut f = f.unwrap();
let eof = f.seek(SeekFrom::End(0));
if eof.is_err() {
return;
}
self.cur_size.set(eof.unwrap() as i64);
fc.replace(Some(f));
}
let mut f = fc.get_mut().as_mut().unwrap();
let now_str = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
let log_line = format!("{}[{}] {}\n", self.prefix.as_str(), now_str.as_str(), s.into());
let _ = f.write_all(log_line.as_bytes());
let _ = f.flush();
self.cur_size.fetch_add(log_line.len() as i64);
let f = l.file.as_mut().unwrap();
let now_str = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
let ss: &str = s.as_ref();
let log_line = format!("{}[{}] {}\n", self.prefix.as_str(), now_str.as_str(), ss);
let _ = f.write_all(log_line.as_bytes());
let _ = f.flush();
l.cur_size += log_line.len() as u64;
}
}
}
unsafe impl Sync for Log {}
/*
#[cfg(test)]
mod tests {
use crate::log::Log;
#[test]
fn test_log() {
let l = Log::new("/tmp/ztlogtest.log", 65536, "");
for i in 0..100000 {
l.log(format!("line {}", i))
}
}
}
*/

View file

@ -29,11 +29,14 @@ use std::boxed::Box;
use std::ffi::CStr;
use std::path::Path;
use crate::store::Store;
use std::sync::Arc;
fn main() {
let mut process_exit_value: i32 = 0;
let mut zerotier_path = unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), 256) };
let mut cli_args = Some(Box::new(cli::parse_cli_args()));
let mut cli_args = Some(Box::new(cli::parse_cli_args()));
let mut zerotier_path = unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), 256) };
let json_output;
let mut token: Option<String> = None;
let mut token_path = Path::new(&zerotier_path).join("authtoken.secret");
@ -54,6 +57,13 @@ fn main() {
}
}
let store = Store::new(zerotier_path.as_str());
if store.is_err() {
println!("FATAL: error accessing directory '{}': {}", zerotier_path, store.err().unwrap().to_string());
std::process::exit(1);
}
let store = Arc::new(store.unwrap());
match cli_args.as_ref().unwrap().subcommand_name().unwrap() {
"version" => {
let ver = zerotier_core::version();
@ -61,7 +71,7 @@ fn main() {
},
"service" => {
cli_args = None; // free any memory we can when launching service
process_exit_value = commands::service::run()
process_exit_value = commands::service::run(&store);
},
_ => cli::print_help(), // includes "help"
}

View file

@ -12,20 +12,36 @@
/****/
use std::error::Error;
use std::path::{Path, PathBuf};
use zerotier_core::StateObjectType;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::ffi::CString;
use zerotier_core::{StateObjectType, NetworkId};
use crate::localconfig::LocalConfig;
pub(crate) struct Store {
pub base_path: Box<Path>,
pub peers_path: Box<Path>,
pub controller_path: Box<Path>,
pub networks_path: Box<Path>,
pub certs_path: Box<Path>,
pub default_log_path: Box<Path>,
peers_path: Box<Path>,
controller_path: Box<Path>,
networks_path: Box<Path>,
certs_path: Box<Path>,
}
/// Restrict file permissions using OS-specific code in osdep/OSUtils.cpp.
pub fn lock_down_file(path: &str) {
let p = CString::new(path.as_bytes());
if p.is_ok() {
let p = p.unwrap();
unsafe {
crate::osdep::lockDownFile(p.as_ptr(), 0);
}
}
}
impl Store {
const MAX_OBJECT_SIZE: usize = 131072; // sanity limit
const MAX_OBJECT_SIZE: usize = 262144; // sanity limit
pub fn new(base_path: &str) -> std::io::Result<Store> {
let bp = Path::new(base_path);
@ -33,73 +49,180 @@ impl Store {
if !md.is_dir() || md.permissions().readonly() {
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "base path does not exist or is not writable"));
}
Ok(Store{
let s = Store {
base_path: bp.to_path_buf().into_boxed_path(),
default_log_path: bp.join("service.log").into_boxed_path(),
peers_path: bp.join("peers.d").into_boxed_path(),
controller_path: bp.join("controller.d").into_boxed_path(),
networks_path: bp.join("networks.d").into_boxed_path(),
certs_path: bp.join("certs.d").into_boxed_path(),
})
};
let _ = std::fs::create_dir_all(&s.peers_path);
let _ = std::fs::create_dir_all(&s.controller_path);
let _ = std::fs::create_dir_all(&s.networks_path);
let _ = std::fs::create_dir_all(&s.certs_path);
Ok(s)
}
fn make_obj_path(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option<PathBuf> {
Some(match obj_type {
StateObjectType::IdentityPublic => self.base_path.join("identity.public"),
StateObjectType::IdentitySecret => self.base_path.join("identity.secret"),
fn make_obj_path(&self, obj_type: &StateObjectType, obj_id: &[u64]) -> Option<PathBuf> {
match obj_type {
StateObjectType::IdentityPublic => {
Some(self.base_path.join("identity.public"))
},
StateObjectType::IdentitySecret => {
Some(self.base_path.join("identity.secret"))
},
StateObjectType::Certificate => {
if obj_id.len() < 6 {
return None;
None
} else {
Some(self.certs_path.join(format!("{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}.cert", obj_id[0], obj_id[1], obj_id[2], obj_id[3], obj_id[4], obj_id[5])))
}
self.certs_path.join(format!("{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}{:0>16x}.cert",obj_id[0],obj_id[1],obj_id[2],obj_id[3],obj_id[4],obj_id[5]))
},
StateObjectType::TrustStore => self.base_path.join("truststore"),
StateObjectType::Locator => self.base_path.join("locator"),
StateObjectType::TrustStore => {
Some(self.base_path.join("truststore"))
},
StateObjectType::Locator => {
Some(self.base_path.join("locator"))
},
StateObjectType::NetworkConfig => {
if obj_id.len() < 1 {
return None;
None
} else {
Some(self.networks_path.join(format!("{:0>16x}.conf", obj_id[0])))
}
self.networks_path.join(format!("{:0>16x}.conf", obj_id[0]))
},
StateObjectType::Peer => {
if obj_id.len() < 1 {
return None;
None
} else {
Some(self.peers_path.join(format!("{:0>10x}.peer", obj_id[0])))
}
self.peers_path.join(format!("{:0>10x}.peer", obj_id[0]))
}
})
}
}
pub fn load(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result<Box<[u8]>> {
let obj_path = self.make_obj_path(obj_type, obj_id);
if obj_path.is_some() {
let obj_path = obj_path.unwrap();
let fmd = obj_path.metadata()?;
if fmd.is_file() {
let flen = fmd.len();
if flen <= Store::MAX_OBJECT_SIZE as u64 {
let mut f = std::fs::File::open(obj_path)?;
let mut buf: Vec<u8> = Vec::new();
buf.reserve(flen as usize);
let rs = f.read_to_end(&mut buf)?;
buf.resize(rs as usize, 0);
return Ok(buf.into_boxed_slice());
}
/// Class-internal read function
fn read_internal(&self, path: PathBuf) -> std::io::Result<Vec<u8>> {
let fmd = path.metadata()?;
if fmd.is_file() {
let flen = fmd.len();
if flen <= Store::MAX_OBJECT_SIZE as u64 {
let mut f = std::fs::File::open(path)?;
let mut buf: Vec<u8> = Vec::new();
buf.reserve(flen as usize);
let rs = f.read_to_end(&mut buf)?;
buf.resize(rs as usize, 0);
return Ok(buf);
}
}
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "does not exist or is not readable"))
}
pub fn erase(&self, obj_type: StateObjectType, obj_id: &[u64]) {
/// Get a list of the network IDs to which this node is joined.
/// This is used to recall networks on startup by enumerating networks.d
/// and telling the core to (re)join them all.
pub fn networks(&self) -> Vec<NetworkId> {
let mut list: Vec<NetworkId> = Vec::new();
let d = std::fs::read_dir(self.networks_path.as_ref());
if d.is_ok() {
for de in d.unwrap() {
if de.is_ok() {
let nn = de.unwrap().file_name();
let n = nn.to_str().unwrap_or("");
if n.len() == 21 && n.ends_with(".conf") { // ################.conf
let nwid = u64::from_str_radix(&n[0..16], 16);
if nwid.is_ok() {
list.push(NetworkId(nwid.unwrap()));
}
}
}
}
}
list
}
/// Read a file located in the base ZeroTier home directory.
pub fn read_file(&self, fname: &str) -> std::io::Result<Vec<u8>> {
self.read_internal(self.base_path.join(fname))
}
/// Like read_file but also converts into a string.
pub fn read_file_str(&self, fname: &str) -> std::io::Result<String> {
let data = self.read_file(fname)?;
let data = String::from_utf8(data);
if data.is_err() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, data.err().unwrap().to_string()));
}
Ok(data.unwrap())
}
/// Write a file to the base ZeroTier home directory.
pub fn write_file(&self, fname: &str, data: &[u8]) -> std::io::Result<()> {
std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(self.base_path.join(fname))?.write_all(data)
}
/// Reads local.conf and deserializes into a LocalConfig object.
pub fn read_local_conf(&self) -> std::io::Result<LocalConfig> {
let data = self.read_file_str("local.conf")?;
let lc = LocalConfig::new_from_json(data.as_str());
if lc.is_err() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, lc.err().unwrap()));
}
Ok(lc.unwrap())
}
/// Writes a LocalConfig object in JSON format to local.conf.
pub fn write_local_conf(&self, lc: &LocalConfig) -> std::io::Result<()> {
let json = lc.to_json();
self.write_file("local.conf", json.as_bytes())
}
/// Reads the authtoken.secret file in the home directory.
pub fn read_authtoken_secret(&self) -> std::io::Result<String> {
let data = self.read_file_str("authtoken.secret")?;
Ok(data.trim().to_string())
}
/// Write authtoken.secret and lock down file permissions.
pub fn write_authtoken_secret(&self, sec: &str) -> std::io::Result<()> {
let p = self.base_path.join("authtoken.secret");
let _ = std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(&p)?.write_all(sec.as_bytes())?;
lock_down_file(p.to_str().unwrap());
Ok(())
}
/// Load a ZeroTier core object.
pub fn load_object(&self, obj_type: &StateObjectType, obj_id: &[u64]) -> std::io::Result<Vec<u8>> {
let obj_path = self.make_obj_path(&obj_type, obj_id);
if obj_path.is_some() {
return self.read_internal(obj_path.unwrap());
}
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "does not exist or is not readable"))
}
/// Erase a ZeroTier core object.
pub fn erase_object(&self, obj_type: &StateObjectType, obj_id: &[u64]) {
let obj_path = self.make_obj_path(obj_type, obj_id);
if obj_path.is_some() {
let _ = std::fs::remove_file(obj_path.unwrap());
}
}
pub fn store(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> {
/// Store a ZeroTier core object.
/// Permissions will also be restricted for some object types.
pub fn store_object(&self, obj_type: &StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> {
let obj_path = self.make_obj_path(obj_type, obj_id);
if obj_path.is_some() {
std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(obj_path.unwrap())?.write_all(obj_data)
let obj_path = obj_path.unwrap();
std::fs::OpenOptions::new().write(true).truncate(true).create(true).open(&obj_path)?.write_all(obj_data)?;
if obj_type.eq(&StateObjectType::IdentitySecret) || obj_type.eq(&StateObjectType::TrustStore) {
lock_down_file(obj_path.to_str().unwrap());
}
Ok(())
} else {
Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "object ID not valid"))
}