Rust service work in progress...

This commit is contained in:
Adam Ierymenko 2021-02-03 14:41:42 -05:00
parent c29a64602a
commit f9deec7872
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
33 changed files with 598 additions and 1110 deletions

View file

@ -25,8 +25,6 @@
#include "Containers.hpp"
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
#define ZT_IDENTITY_C25519_PUBLIC_KEY_SIZE ZT_C25519_COMBINED_PUBLIC_KEY_SIZE
#define ZT_IDENTITY_C25519_PRIVATE_KEY_SIZE ZT_C25519_COMBINED_PRIVATE_KEY_SIZE
#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (7 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + ZT_ECC384_PUBLIC_KEY_SIZE)
#define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_COMBINED_PRIVATE_KEY_SIZE + ZT_ECC384_PRIVATE_KEY_SIZE)
#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)

View file

@ -1,117 +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_BLOCKINGQUEUE_HPP
#define ZT_BLOCKINGQUEUE_HPP
#include <queue>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <atomic>
#include "../core/Constants.hpp"
namespace ZeroTier {
/**
* Simple C++11 thread-safe queue
*
* Do not use in core/ since we have not gone C++11 there yet.
*/
template <class T>
class BlockingQueue
{
public:
enum TimedWaitResult
{
OK,
TIMED_OUT,
STOP
};
ZT_INLINE BlockingQueue(void) : r(true) {}
ZT_INLINE void post(T t)
{
std::lock_guard<std::mutex> lock(m);
q.push(t);
c.notify_one();
}
ZT_INLINE void postLimit(T t,const unsigned long limit)
{
std::unique_lock<std::mutex> lock(m);
for(;;) {
if (q.size() < limit) {
q.push(t);
c.notify_one();
break;
}
if (!r)
break;
gc.wait(lock);
}
}
ZT_INLINE void stop(void)
{
std::lock_guard<std::mutex> lock(m);
r = false;
c.notify_all();
gc.notify_all();
}
ZT_INLINE bool get(T &value)
{
std::unique_lock<std::mutex> lock(m);
if (!r) return false;
while (q.empty()) {
c.wait(lock);
if (!r) {
gc.notify_all();
return false;
}
}
value = q.front();
q.pop();
gc.notify_all();
return true;
}
ZT_INLINE TimedWaitResult get(T &value,const unsigned long ms)
{
const std::chrono::milliseconds ms2{ms};
std::unique_lock<std::mutex> lock(m);
if (!r) return STOP;
while (q.empty()) {
if (c.wait_for(lock,ms2) == std::cv_status::timeout)
return ((r) ? TIMED_OUT : STOP);
else if (!r)
return STOP;
}
value = q.front();
q.pop();
return OK;
}
private:
std::atomic_bool r;
std::queue<T> q;
mutable std::mutex m;
mutable std::condition_variable c,gc;
};
} // namespace ZeroTier
#endif

View file

@ -8,10 +8,10 @@ set(src
)
set(headers
BlockingQueue.hpp
ManagedRoute.hpp
OSUtils.hpp
Thread.hpp
rust-osdep.h
)
if(WIN32)
@ -27,11 +27,6 @@ elseif(UNIX)
set(src ${src} freebsd_getifmaddrs.c)
set(headers ${headers} freebsd_getifmaddrs.h)
endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(src ${src} LinuxEthernetTap.cpp LinuxNetLink.cpp)
set(headers ${headers} LinuxEthernetTap.hpp LinuxNetLink.hpp)
endif(CMAKE_SYSTEM_NAME MATCHES "Linux")
endif(WIN32)
add_library(${PROJECT_NAME} STATIC ${src} ${headers})

View file

@ -1,555 +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.
*/
/****/
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
#include "../node/Constants.hpp"
#ifdef __LINUX__
#include "../node/Utils.hpp"
#include "../node/Mutex.hpp"
#include "../node/Dictionary.hpp"
#include "OSUtils.hpp"
#include "LinuxEthernetTap.hpp"
#include "LinuxNetLink.hpp"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_addr.h>
#include <linux/if_ether.h>
#include <ifaddrs.h>
#include <algorithm>
#include <utility>
#include <string>
#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif
#define ZT_TAP_BUF_SIZE 16384
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
namespace ZeroTier {
static const char _base32_chars[32] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','2','3','4','5','6','7' };
static void _base32_5_to_8(const uint8_t *in,char *out)
{
out[0] = _base32_chars[(in[0]) >> 3];
out[1] = _base32_chars[(in[0] & 0x07) << 2 | (in[1] & 0xc0) >> 6];
out[2] = _base32_chars[(in[1] & 0x3e) >> 1];
out[3] = _base32_chars[(in[1] & 0x01) << 4 | (in[2] & 0xf0) >> 4];
out[4] = _base32_chars[(in[2] & 0x0f) << 1 | (in[3] & 0x80) >> 7];
out[5] = _base32_chars[(in[3] & 0x7c) >> 2];
out[6] = _base32_chars[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5];
out[7] = _base32_chars[(in[4] & 0x1f)];
}
LinuxEthernetTap::LinuxEthernetTap(
const char *homePath,
const MAC &mac,
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),
_mac(mac),
_homePath(homePath),
_mtu(mtu),
_fd(0),
_enabled(true)
{
static std::mutex s_tapCreateLock;
char procpath[128],nwids[32];
struct stat sbuf;
// Create only one tap at a time globally.
std::lock_guard<std::mutex> tapCreateLock(s_tapCreateLock);
// Make sure Linux netlink is initialized.
(void)LinuxNetLink::getInstance();
OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
_fd = ::open("/dev/net/tun",O_RDWR);
if (_fd <= 0) {
_fd = ::open("/dev/tun",O_RDWR);
if (_fd <= 0)
throw std::runtime_error(std::string("could not open TUN/TAP device: ") + strerror(errno));
}
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
// Restore device names from legacy devicemap, but for new devices we use a base32-based
// canonical device name.
std::map<std::string,std::string> globalDeviceMap;
FILE *devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"r");
if (devmapf) {
char buf[256];
while (fgets(buf,sizeof(buf),devmapf)) {
char *x = (char *)0;
char *y = (char *)0;
char *saveptr = (char *)0;
for(char *f=Utils::stok(buf,"\r\n=",&saveptr);(f);f=Utils::stok((char *)0,"\r\n=",&saveptr)) {
if (!x) x = f;
else if (!y) y = f;
else break;
}
if ((x)&&(y)&&(x[0])&&(y[0]))
globalDeviceMap[x] = y;
}
fclose(devmapf);
}
bool recalledDevice = false;
std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids);
if (gdmEntry != globalDeviceMap.end()) {
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),gdmEntry->second.c_str());
OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
recalledDevice = (stat(procpath,&sbuf) != 0);
}
if (!recalledDevice) {
#ifdef __SYNOLOGY__
int devno = 50;
do {
OSUtils::ztsnprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
} while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
#else
uint64_t trial = 0; // incremented in the very unlikely event of a name collision with another network
do {
const uint64_t nwid40 = (nwid ^ (nwid >> 24)) + trial++;
uint8_t tmp2[5];
char tmp3[11];
tmp2[0] = (uint8_t)((nwid40 >> 32) & 0xff);
tmp2[1] = (uint8_t)((nwid40 >> 24) & 0xff);
tmp2[2] = (uint8_t)((nwid40 >> 16) & 0xff);
tmp2[3] = (uint8_t)((nwid40 >> 8) & 0xff);
tmp2[4] = (uint8_t)(nwid40 & 0xff);
tmp3[0] = 'z';
tmp3[1] = 't';
_base32_5_to_8(tmp2,tmp3 + 2);
tmp3[10] = (char)0;
memcpy(ifr.ifr_name,tmp3,11);
OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
} while (stat(procpath,&sbuf) == 0);
#endif
}
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ioctl(_fd,TUNSETIFF,(void *)&ifr) < 0) {
::close(_fd);
throw std::runtime_error("unable to configure TUN/TAP device for TAP operation");
}
::ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here
_dev = ifr.ifr_name;
::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
(void)::pipe(_shutdownSignalPipe);
_tapReaderThread = std::thread([this]{
fd_set readfds,nullfds;
int n,nfds,r;
void *buf = nullptr;
std::vector<void *> buffers;
{
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
strcpy(ifr.ifr_name,_dev.c_str());
const int sock = socket(AF_INET,SOCK_DGRAM,0);
if (sock <= 0)
return;
if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) {
::close(sock);
printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n");
return;
}
ifr.ifr_flags |= IFF_UP;
if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) {
::close(sock);
printf("WARNING: ioctl() failed setting up Linux tap device (bring interface up)\n");
return;
}
// Some kernel versions seem to require you to yield while the device comes up
// before they will accept MTU and MAC. For others it doesn't matter, but is
// harmless. This was moved to the worker thread though so as not to block the
// main ZeroTier loop.
usleep(500000);
ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER;
_mac.copyTo(ifr.ifr_ifru.ifru_hwaddr.sa_data,6);
if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) {
::close(sock);
printf("WARNING: ioctl() failed setting up Linux tap device (set MAC)\n");
return;
}
ifr.ifr_ifru.ifru_mtu = (int)_mtu;
if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) {
::close(sock);
printf("WARNING: ioctl() failed setting up Linux tap device (set MTU)\n");
return;
}
fcntl(_fd,F_SETFL,O_NONBLOCK);
::close(sock);
}
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)) {
for(;;) { // read until there are no more packets, then return to outer select() loop
if (!buf) {
// To reduce use of the mutex, we keep a local buffer vector and
// swap (which is a pointer swap) with the global one when it's
// empty. This retrieves a batch of buffers to use.
if (buffers.empty()) {
std::lock_guard<std::mutex> l(_buffers_l);
buffers.swap(_buffers);
}
if (buffers.empty()) {
buf = malloc(ZT_TAP_BUF_SIZE);
if (!buf)
break;
} else {
buf = buffers.back();
buffers.pop_back();
}
}
n = (int)::read(_fd,reinterpret_cast<uint8_t *>(buf) + r,ZT_TAP_BUF_SIZE - r);
if (n > 0) {
// 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) {
_tapq.post(std::pair<void *,int>(buf,r));
buf = nullptr;
}
r = 0;
}
} else {
r = 0;
break;
}
}
}
}
});
_tapProcessorThread = std::thread([this] {
MAC to,from;
std::pair<void *,int> qi;
while (_tapq.get(qi)) {
uint8_t *const b = reinterpret_cast<uint8_t *>(qi.first);
if (b) {
to.setTo(b, 6);
from.setTo(b + 6, 6);
unsigned int etherType = Utils::ntoh(((const uint16_t *)b)[6]);
_handler(_arg, nullptr, _nwid, from, to, etherType, 0, (const void *)(b + 14),(unsigned int)(qi.second - 14));
{
std::lock_guard<std::mutex> l(_buffers_l);
if (_buffers.size() < 128)
_buffers.push_back(qi.first);
else free(qi.first);
}
} else break;
}
});
}
LinuxEthernetTap::~LinuxEthernetTap()
{
(void)::write(_shutdownSignalPipe[1],"\0",1); // causes reader thread to exit
_tapq.post(std::pair<void *,int>(nullptr,0)); // causes processor thread to exit
::close(_fd);
::close(_shutdownSignalPipe[0]);
::close(_shutdownSignalPipe[1]);
_tapReaderThread.join();
_tapProcessorThread.join();
for(std::vector<void *>::iterator i(_buffers.begin());i!=_buffers.end();++i)
free(*i);
std::vector< std::pair<void *,int> > dv(_tapq.drain());
for(std::vector< std::pair<void *,int> >::iterator i(dv.begin());i!=dv.end();++i) {
if (i->first)
free(i->first);
}
}
void LinuxEthernetTap::setEnabled(bool en)
{
_enabled = en;
}
bool LinuxEthernetTap::enabled() const
{
return _enabled;
}
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
{
LinuxNetLink::getInstance().removeAddress(ip, _dev.c_str());
return true;
}
bool LinuxEthernetTap::addIps(std::vector<InetAddress> ips)
{
#ifdef __SYNOLOGY__
std::string filepath = "/etc/sysconfig/network-scripts/ifcfg-"+_dev;
std::string cfg_contents = "DEVICE="+_dev+"\nBOOTPROTO=static";
int ip4=0,ip6=0,ip4_tot=0,ip6_tot=0;
for(int i=0; i<(int)ips.size(); i++) {
if (ips[i].isV4())
ip4_tot++;
else
ip6_tot++;
}
// Assemble and write contents of ifcfg-dev file
for(int i=0; i<(int)ips.size(); i++) {
if (ips[i].isV4()) {
char iptmp[64],iptmp2[64];
std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : "";
cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString(iptmp)
+ "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
ip4++;
} else {
char iptmp[64],iptmp2[64];
std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : "";
cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString(iptmp)
+ "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
ip6++;
}
}
OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length());
// Finally, add IPs
for(int i=0; i<(int)ips.size(); i++){
LinuxNetLink::getInstance().addAddress(ips[i], _dev.c_str());
}
return true;
#endif // __SYNOLOGY__
return false;
}
bool LinuxEthernetTap::addIp(const InetAddress &ip)
{
if (!ip)
return false;
std::vector<InetAddress> allIps(ips());
if (std::binary_search(allIps.begin(),allIps.end(),ip))
return true;
// Remove and reconfigure if address is the same but netmask is different
for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
if (i->ipsEqual(ip))
___removeIp(_dev,*i);
}
LinuxNetLink::getInstance().addAddress(ip, _dev.c_str());
return true;
}
bool LinuxEthernetTap::removeIp(const InetAddress &ip)
{
if (!ip)
return true;
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> LinuxEthernetTap::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());
r.erase(std::unique(r.begin(),r.end()),r.end());
return r;
}
void LinuxEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
char putBuf[ZT_MAX_MTU + 64];
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;
(void)::write(_fd,putBuf,len);
}
}
std::string LinuxEthernetTap::deviceName() const
{
return _dev;
}
void LinuxEthernetTap::setFriendlyName(const char *friendlyName)
{
}
void LinuxEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
{
char *ptr,*ptr2;
unsigned char mac[6];
std::vector<MulticastGroup> newGroups;
int fd = ::open("/proc/net/dev_mcast",O_RDONLY);
if (fd > 0) {
char buf[131072];
int n = (int)::read(fd,buf,sizeof(buf));
if ((n > 0)&&(n < (int)sizeof(buf))) {
buf[n] = (char)0;
for(char *l=strtok_r(buf,"\r\n",&ptr);(l);l=strtok_r((char *)0,"\r\n",&ptr)) {
int fno = 0;
char *devname = (char *)0;
char *mcastmac = (char *)0;
for(char *f=strtok_r(l," \t",&ptr2);(f);f=strtok_r((char *)0," \t",&ptr2)) {
if (fno == 1)
devname = f;
else if (fno == 4)
mcastmac = f;
++fno;
}
if ((devname)&&(!strcmp(devname,_dev.c_str()))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6))
newGroups.push_back(MulticastGroup(MAC(mac,6),0));
}
}
::close(fd);
}
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());
newGroups.erase(std::unique(newGroups.begin(),newGroups.end()),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 LinuxEthernetTap::setMtu(unsigned int mtu)
{
if (_mtu != mtu) {
_mtu = mtu;
int sock = socket(AF_INET,SOCK_DGRAM,0);
if (sock > 0) {
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
ifr.ifr_ifru.ifru_mtu = (int)mtu;
ioctl(sock,SIOCSIFMTU,(void *)&ifr);
close(sock);
}
}
}
} // namespace ZeroTier
#endif // __LINUX__

View file

@ -1,82 +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_LINUXETHERNETTAP_HPP
#define ZT_LINUXETHERNETTAP_HPP
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <stdexcept>
#include <atomic>
#include <array>
#include <thread>
#include <mutex>
#include "../node/MulticastGroup.hpp"
#include "EthernetTap.hpp"
#include "BlockingQueue.hpp"
namespace ZeroTier {
class LinuxEthernetTap : public EthernetTap
{
public:
LinuxEthernetTap(
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 ~LinuxEthernetTap();
virtual void setEnabled(bool en);
virtual bool enabled() const;
virtual bool addIp(const InetAddress &ip);
virtual bool addIps(std::vector<InetAddress> ips);
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);
virtual void setDns(const char *domain, const std::vector<InetAddress> &servers) {}
private:
void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
uint64_t _nwid;
MAC _mac;
std::string _homePath;
std::string _dev;
std::vector<MulticastGroup> _multicastGroups;
unsigned int _mtu;
int _fd;
int _shutdownSignalPipe[2];
std::atomic_bool _enabled;
std::thread _tapReaderThread;
std::thread _tapProcessorThread;
std::mutex _buffers_l;
std::vector<void *> _buffers;
BlockingQueue< std::pair<void *,int> > _tapq;
};
} // namespace ZeroTier
#endif

View file

@ -12,7 +12,6 @@
/****/
#include "../core/Constants.hpp"
#include "../core/Utils.hpp"
#include "../core/Containers.hpp"
#include "OSUtils.hpp"

View file

@ -1,4 +1,8 @@
#include "../core/Constants.hpp"
#include "../core/Mutex.hpp"
#include "../core/Containers.hpp"
#include "OSUtils.hpp"
#include "rust-osdep.h"
#ifdef __APPLE__
@ -13,16 +17,91 @@
extern "C" {
#ifdef __APPLE__
extern const unsigned long c_BIOCSBLEN = BIOCSBLEN;
extern const unsigned long c_BIOCIMMEDIATE = BIOCIMMEDIATE;
extern const unsigned long c_BIOCSSEESENT = BIOCSSEESENT;
extern const unsigned long c_BIOCSETIF = BIOCSETIF;
extern const unsigned long c_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
extern const unsigned long c_BIOCPROMISC = BIOCPROMISC;
extern const unsigned long c_SIOCGIFINFO_IN6 = SIOCGIFINFO_IN6;
extern const unsigned long c_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
extern const unsigned long c_SIOCAUTOCONF_START = SIOCAUTOCONF_START;
extern const unsigned long c_SIOCAUTOCONF_STOP = SIOCAUTOCONF_STOP;
const unsigned long c_BIOCSBLEN = BIOCSBLEN;
const unsigned long c_BIOCIMMEDIATE = BIOCIMMEDIATE;
const unsigned long c_BIOCSSEESENT = BIOCSSEESENT;
const unsigned long c_BIOCSETIF = BIOCSETIF;
const unsigned long c_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
const unsigned long c_BIOCPROMISC = BIOCPROMISC;
const unsigned long c_SIOCGIFINFO_IN6 = SIOCGIFINFO_IN6;
const unsigned long c_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
const unsigned long c_SIOCAUTOCONF_START = SIOCAUTOCONF_START;
const unsigned long c_SIOCAUTOCONF_STOP = SIOCAUTOCONF_STOP;
#endif
const char *platformDefaultHomePath()
{
static ZeroTier::Mutex s_lock;
static ZeroTier::String s_homePath;
ZeroTier::Mutex::Lock l(s_lock);
if (s_homePath.empty()) {
#ifdef __QNAP__
char *cmd = "/sbin/getcfg zerotier Install_Path -f /etc/config/qpkg.conf";
char buf[128];
FILE *fp;
if ((fp = popen(cmd, "r")) == NULL) {
printf("Error opening pipe!\n");
return NULL;
}
while (fgets(buf, 128, fp) != NULL) { }
if(pclose(fp)) {
printf("Command not found or exited with error status\n");
return NULL;
}
String homeDir = String(buf);
homeDir.erase(std::remove(homeDir.begin(), homeDir.end(), '\n'), homeDir.end());
s_homePath = homeDir;
#else
#ifdef __WINDOWS__
DWORD bufferSize = 65535;
ZeroTier::String userDefinedPath;
bufferSize = GetEnvironmentVariable("ZEROTIER_HOME", &userDefinedPath[0], bufferSize);
if (bufferSize) {
s_homePath = userDefinedPath;
} else {
char buf[16384];
if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf))) {
ZeroTier::String tmp(buf);
tmp.append("\\ZeroTier");
s_homePath = tmp;
} else {
s_homePath = "C:\\ZeroTier";
}
}
#else
if (const char *userDefinedPath = getenv("ZEROTIER_HOME")) {
s_homePath = userDefinedPath;
} else {
#ifdef __APPLE__
s_homePath = "/Library/Application Support/ZeroTier";
#else
#ifdef __BSD__
s_homePath = "/var/db/zerotier";
#else
s_homePath = "/var/lib/zerotier";
#endif // __BSD__ or not
#endif // __APPLE__ or not
}
#endif // __WINDOWS__ or not
#endif // __QNAP__ or not
if (s_homePath.empty())
s_homePath = "." ZT_PATH_SEPARATOR_S;
}
return s_homePath.c_str();
}
int64_t msSinceEpoch()
{ return ZeroTier::OSUtils::now(); }
}

View file

@ -46,3 +46,14 @@ extern const unsigned long c_SIOCAUTOCONF_STOP;
}
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern const char *platformDefaultHomePath();
extern int64_t msSinceEpoch();
#ifdef __cplusplus
}
#endif

View file

@ -1,6 +1,8 @@
fn main() {
let d = env!("CARGO_MANIFEST_DIR");
println!("cargo:rustc-link-search=native={}/../build/core", d);
println!("cargo:rustc-link-search=native={}/../build/osdep", d);
println!("cargo:rustc-link-lib=static=zt_core");
println!("cargo:rustc-link-lib=static=zt_osdep");
println!("cargo:rustc-link-lib=c++");
}

View file

@ -1,3 +1,16 @@
/*
* 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.
*/
/****/
use std::str::FromStr;
use clap::{App, Arg, ArgMatches, ErrorKind};

View file

@ -0,0 +1,24 @@
/*
* 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.
*/
/****/
pub(crate) mod service;
pub(crate) mod status;
pub(crate) mod set;
pub(crate) mod peer;
pub(crate) mod network;
pub(crate) mod join;
pub(crate) mod leave;
pub(crate) mod controller;
pub(crate) mod identity;
pub(crate) mod locator;
pub(crate) mod cert;

View file

@ -0,0 +1,206 @@
/*
* 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.
*/
/****/
use std::collections::BTreeMap;
use std::net::IpAddr;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use futures::stream::{self, StreamExt};
use warp::Filter;
use warp::hyper::{HeaderMap, Method};
use warp::hyper::body::Bytes;
use zerotier_core::*;
use crate::fastudpsocket::*;
use crate::localconfig::*;
use crate::network::Network;
use crate::getifaddrs;
struct ServiceEventHandler {}
impl NodeEventHandler<Network> for ServiceEventHandler {
fn virtual_network_config(&self, network_id: NetworkId, network_obj: &Arc<Network>, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>) {
}
#[inline(always)]
fn virtual_network_frame(&self, network_id: NetworkId, network_obj: &Arc<Network>, source_mac: MAC, dest_mac: MAC, ethertype: u16, vlan_id: u16, data: &[u8]) {
}
fn event(&self, event: Event, event_data: &[u8]) {
}
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) {
}
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option<Box<[u8]>> {
None
}
#[inline(always)]
fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32 {
0
}
#[inline(always)]
fn path_check(&self, address: Address, id: &Identity, local_socket: i64, sock_addr: &InetAddress) -> bool {
true
}
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress> {
None
}
}
pub(crate) fn run() -> i32 {
let inaddr_v6_any = IpAddr::from_str("::0").unwrap();
let mut process_exit_value: i32 = 0;
// Current active local configuration for this node.
let mut local_config: Box<LocalConfig> = Box::new(LocalConfig::default());
// Event handler for Node.
let handler: Arc<ServiceEventHandler> = Arc::new(ServiceEventHandler{});
// From this point on we are in Tokio async land...
let tokio_rt = tokio::runtime::Builder::new_multi_thread().thread_stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).build().unwrap();
tokio_rt.block_on(async {
// Keeps track of FastUDPSocket instances by bound address.
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
// Send something to interrupt_tx to interrupt the inner loop and force it to
// detect a change or exit if run has been set to false.
let (mut interrupt_tx, mut interrupt_rx) = futures::channel::mpsc::channel::<u8>(2);
// Setting this to false terminates the service. It's atomic since this is multithreaded.
let run = AtomicBool::new(true);
loop {
let mut warp_server_port = local_config.settings.primary_port;
let root = warp::path::end().map(|| {
warp::reply::with_status("404", warp::hyper::StatusCode::NOT_FOUND)
});
let status = warp::path("status")
.and(warp::method())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.map(|method: Method, headers: HeaderMap, post_data: Bytes| {
"status"
});
let network = warp::path!("network" / String)
.and(warp::method())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.map(|nwid_str: String, method: Method, headers: HeaderMap, post_data: Bytes| {
"network"
});
let peer = warp::path!("peer" / String)
.and(warp::method())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.map(|peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes| {
"peer"
});
let (mut shutdown_tx, mut shutdown_rx) = futures::channel::oneshot::channel();
let warp_server = warp::serve(warp::any().and(root
.or(status)
.or(network)
.or(peer)
)).try_bind_with_graceful_shutdown((inaddr_v6_any, warp_server_port), async { let _ = shutdown_rx.await; });
if warp_server.is_err() {
// TODO: log unable to bind to primary port
run.store(false, Ordering::Relaxed);
}
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)) => {},
_ = interrupt_rx.next() => {},
_ = tokio::signal::ctrl_c() => {
// TODO: log CTRL+C received
run.store(false, Ordering::Relaxed);
let _ = shutdown_tx.send(());
break;
}
}
// Enumerate physical addresses on the system, creating a map with an entry for
// the primary_port and another for the secondary_port if bound.
let mut system_addrs: BTreeMap<InetAddress, String> = BTreeMap::new();
getifaddrs::for_each_address(|addr: &InetAddress, dev: &str| {
if !local_config.settings.is_interface_blacklisted(dev) {
let mut a = addr.clone();
a.set_port(local_config.settings.primary_port);
system_addrs.insert(a, String::from(dev));
if local_config.settings.secondary_port.is_some() {
let mut a = addr.clone();
a.set_port(local_config.settings.secondary_port.unwrap());
system_addrs.insert(a, String::from(dev));
}
}
});
// Close UDP bindings that no longer apply.
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);
}
// Bind addresses that are not already bound.
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());
}
}
}
// TODO: check that ports are bound, implement port hunting or exit.
if local_config.settings.primary_port != warp_server_port || !run.load(Ordering::Relaxed) {
let _ = shutdown_tx.send(());
break;
}
}
let _ = warp_server.await;
if !run.load(Ordering::Relaxed) {
break;
}
tokio::time::sleep(Duration::from_millis(250)).await;
if !run.load(Ordering::Relaxed) {
break;
}
}
});
process_exit_value
}

View file

@ -173,6 +173,8 @@ impl FastUDPSocket {
sockets: Vec::new(),
bind_address: address.clone()
};
s.threads.reserve(thread_count);
s.sockets.reserve(thread_count);
let mut bind_failed_reason: &'static str = "";
for _ in 0..thread_count {
@ -211,6 +213,12 @@ impl FastUDPSocket {
Ok(s)
}
/// Get a slice of all raw sockets used.
#[inline(always)]
pub fn all_sockets(&self) -> &[FastUDPRawOsSocket] {
self.sockets.as_slice()
}
/// Send from this socket.
/// This actually picks a thread's socket and sends from it. Since all
/// are bound to the same IP:port which one is chosen doesn't matter.

View file

@ -0,0 +1,76 @@
/*
* 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.
*/
/****/
use zerotier_core::InetAddress;
use std::ffi::CStr;
use std::ptr::{null_mut, copy_nonoverlapping};
use std::mem::size_of;
use num_traits::cast::AsPrimitive;
use crate::osdep as osdep;
#[inline(always)]
fn s6_addr_as_ptr<A>(a: &A) -> *const A {
a as *const A
}
/// Call supplied function or closure for each physical IP address in the system.
#[cfg(unix)]
pub(crate) fn for_each_address<F: FnMut(&InetAddress, &str)>(mut f: F) {
unsafe {
let mut ifap: *mut osdep::ifaddrs = null_mut();
if osdep::getifaddrs((&mut ifap as *mut *mut osdep::ifaddrs).cast()) == 0 {
let mut i = ifap;
while !i.is_null() {
if !(*i).ifa_addr.is_null() {
let mut a = InetAddress::new();
let sa_family = (*(*i).ifa_addr).sa_family as u8;
if sa_family == osdep::AF_INET as u8 {
copy_nonoverlapping((*i).ifa_addr.cast::<u8>(), (&mut a as *mut InetAddress).cast::<u8>(), size_of::<osdep::sockaddr_in>());
} else if sa_family == osdep::AF_INET6 as u8 {
copy_nonoverlapping((*i).ifa_addr.cast::<u8>(), (&mut a as *mut InetAddress).cast::<u8>(), size_of::<osdep::sockaddr_in6>());
} else {
continue;
}
let mut netmask_bits: u16 = 0;
if !(*i).ifa_netmask.is_null() {
if sa_family == osdep::AF_INET as u8 {
let mut a = (*(*i).ifa_netmask.cast::<osdep::sockaddr_in>()).sin_addr.s_addr as u32;
netmask_bits = a.leading_ones() as u16;
} else if sa_family == osdep::AF_INET6 as u8 {
let a = s6_addr_as_ptr(&((*(*i).ifa_netmask.cast::<osdep::sockaddr_in6>()).sin6_addr)).cast::<u8>();
for i in 0..16 as isize {
let mut b = *a.offset(i);
if b == 0xff {
netmask_bits += 8;
} else {
netmask_bits += b.leading_ones() as u16;
break;
}
}
}
}
a.set_port(netmask_bits);
let dev = CStr::from_ptr((*i).ifa_name).to_str();
if dev.is_ok() {
f(&a, dev.unwrap());
}
}
i = (*i).ifa_next;
}
osdep::freeifaddrs(ifap.cast());
}
}
}

View file

@ -220,7 +220,8 @@ impl LocalConfig {
if md.is_err() {
return Err(md.err().unwrap());
}
if md.unwrap().len() > 1048576 {
if md.unwrap().len() > 1048576 { // anti-memory-overflow sanity limit
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "local config file too large (sanity limit: 1MiB)"))
}
let json = std::fs::read_to_string(path);
if json.is_err() {

View file

@ -12,9 +12,10 @@
/****/
mod cli;
mod commands;
mod fastudpsocket;
mod localconfig;
mod physicallink;
mod getifaddrs;
mod log;
mod store;
mod network;
@ -23,205 +24,51 @@ mod vnic;
#[allow(non_snake_case,non_upper_case_globals,non_camel_case_types,dead_code,improper_ctypes)]
mod osdep;
use std::cell::Cell;
use std::collections::BTreeMap;
use std::net::IpAddr;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use futures::stream::{self, StreamExt};
use warp::Filter;
use warp::hyper::{HeaderMap, Method};
use warp::hyper::body::Bytes;
use zerotier_core::*;
use crate::fastudpsocket::*;
use crate::localconfig::*;
use crate::log::Log;
use crate::physicallink::PhysicalLink;
use crate::network::Network;
pub struct ServiceEventHandler {}
impl NodeEventHandler<Network> for ServiceEventHandler {
fn virtual_network_config(&self, network_id: NetworkId, network_obj: &Arc<Network>, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>) {
}
#[inline(always)]
fn virtual_network_frame(&self, network_id: NetworkId, network_obj: &Arc<Network>, source_mac: MAC, dest_mac: MAC, ethertype: u16, vlan_id: u16, data: &[u8]) {
}
fn event(&self, event: Event, event_data: &[u8]) {
}
fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) {
}
fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> Option<Box<[u8]>> {
None
}
#[inline(always)]
fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32 {
0
}
#[inline(always)]
fn path_check(&self, address: Address, id: &Identity, local_socket: i64, sock_addr: &InetAddress) -> bool {
true
}
fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option<InetAddress> {
None
}
}
use std::boxed::Box;
use std::ffi::CStr;
use std::path::Path;
fn main() {
let cli_args = Some(cli::parse_cli_args());
let inaddr_v6_any = IpAddr::from_str("::0").unwrap();
let mut process_exit_value: i32 = 0;
// Current active local configuration for this node.
let mut local_config: Box<LocalConfig> = Box::new(LocalConfig::default());
let mut zerotier_path;
unsafe {
zerotier_path = zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), 256);
}
// Handler for incoming packets from FastUDPSocket and incoming events from Node.
let handler: Arc<ServiceEventHandler> = Arc::new(ServiceEventHandler{});
let mut cli_args = Some(Box::new(cli::parse_cli_args()));
// From this point on we are in Tokio async land...
let tokio_rt = tokio::runtime::Builder::new_multi_thread().thread_stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).build().unwrap();
tokio_rt.block_on(async {
// Keeps track of FastUDPSocket instances by bound address.
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
// Send something to interrupt_tx to interrupt the inner loop and force it to
// detect a change or exit if run has been set to false.
let (mut interrupt_tx, mut interrupt_rx) = futures::channel::mpsc::channel::<u8>(2);
// Setting this to false terminates the service. It's atomic since this is multithreaded.
let run = AtomicBool::new(true);
loop {
let mut warp_server_port = local_config.settings.primary_port;
let root = warp::path::end().map(|| {
warp::reply::with_status("404", warp::hyper::StatusCode::NOT_FOUND)
});
let status = warp::path("status")
.and(warp::method())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.map(|method: Method, headers: HeaderMap, post_data: Bytes| {
"status"
});
let network = warp::path!("network" / String)
.and(warp::method())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.map(|nwid_str: String, method: Method, headers: HeaderMap, post_data: Bytes| {
"network"
});
let peer = warp::path!("peer" / String)
.and(warp::method())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.map(|peer_str: String, method: Method, headers: HeaderMap, post_data: Bytes| {
"peer"
});
let (mut shutdown_tx, mut shutdown_rx) = futures::channel::oneshot::channel();
let warp_server = warp::serve(warp::any().and(
root
.or(status)
.or(network)
.or(peer)
)).try_bind_with_graceful_shutdown((inaddr_v6_any, warp_server_port), async { let _ = shutdown_rx.await; });
if warp_server.is_err() {
// TODO: log unable to bind to primary port
run.store(false, Ordering::Relaxed);
}
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)) => {},
_ = interrupt_rx.next() => {},
_ = tokio::signal::ctrl_c() => {
// TODO: log CTRL+C received
run.store(false, Ordering::Relaxed);
let _ = shutdown_tx.send(());
break;
}
}
// Enumerate physical addresses on the system, creating a map with an entry for
// the primary_port and another for the secondary_port if bound.
let mut system_addrs: BTreeMap<InetAddress, Rc<PhysicalLink>> = BTreeMap::new();
PhysicalLink::map(|link: PhysicalLink| {
if !local_config.settings.is_interface_blacklisted(link.device.as_str()) {
let l = Rc::new(link);
let mut a = l.address.clone();
a.set_port(local_config.settings.primary_port);
system_addrs.insert(a, l.clone());
if local_config.settings.secondary_port.is_some() {
let mut a = l.address.clone();
a.set_port(local_config.settings.secondary_port.unwrap());
system_addrs.insert(a, l.clone());
}
}
});
// Close UDP bindings that no longer apply.
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);
}
// Bind addresses that are not already bound.
for addr in system_addrs.iter() {
if !udp_sockets.contains_key(addr.0) {
let s = FastUDPSocket::new(addr.1.device.as_str(), addr.0, |raw_socket: &FastUDPRawOsSocket, from_address: &InetAddress, data: Buffer| {
// TODO
});
if s.is_ok() {
udp_sockets.insert(addr.0.clone(), s.unwrap());
} else if addr.0.port() == local_config.settings.primary_port {
run.store(false, Ordering::Relaxed);
// TODO: log failure to bind to primary port (UDP)
break;
}
}
}
if local_config.settings.primary_port != warp_server_port || !run.load(Ordering::Relaxed) {
let _ = shutdown_tx.send(());
break;
}
}
let _ = warp_server.await;
if !run.load(Ordering::Relaxed) {
break;
}
tokio::time::sleep(Duration::from_millis(250)).await;
if !run.load(Ordering::Relaxed) {
break;
}
let json_output;
let mut token: Option<String> = None;
let mut token_path = Path::new(&zerotier_path).join("authtoken.secret");
{
let a = cli_args.unwrap();
json_output = a.is_present("json");
let v = a.value_of("path");
if v.is_some() {
zerotier_path = String::from(v.unwrap());
}
});
let v = a.value_of("token");
if v.is_some() {
token = Some(String::from(v.unwrap().trim()));
}
let v = a.value_of("token_path");
if v.is_some() {
token_path = Path::new(v.unwrap().trim()).into_path_buf();
}
}
match cli_args.unwrap().subcommand_name().unwrap() {
"version" => {
let ver = zerotier_core::version();
println!("{}.{}.{}", ver.0, ver.1, ver.2);
},
"service" => {
cli_args = None; // free any memory we can when launching service
process_exit_value = commands::service::run()
},
_ => cli::print_help(), // includes "help"
}
std::process::exit(process_exit_value);
}

View file

@ -1,3 +1,16 @@
/*
* 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.
*/
/****/
pub struct Network {}
impl Network {

View file

@ -1,82 +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.
*/
/****/
use zerotier_core::InetAddress;
use std::ffi::CStr;
use std::ptr::{null_mut, copy_nonoverlapping};
use std::mem::size_of;
use num_traits::cast::AsPrimitive;
use crate::osdep as osdep;
pub struct PhysicalLink {
pub address: InetAddress,
pub device: String
}
#[inline(always)]
fn s6_addr_as_ptr<A>(a: &A) -> *const A {
a as *const A
}
impl PhysicalLink {
#[cfg(unix)]
pub fn map<F: FnMut(PhysicalLink)>(mut f: F) {
unsafe {
let mut ifap: *mut osdep::ifaddrs = null_mut();
if osdep::getifaddrs((&mut ifap as *mut *mut osdep::ifaddrs).cast()) == 0 {
let mut i = ifap;
while !i.is_null() {
if !(*i).ifa_addr.is_null() {
let mut a = InetAddress::new();
let sa_family = (*(*i).ifa_addr).sa_family as u8;
if sa_family == osdep::AF_INET as u8 {
copy_nonoverlapping((*i).ifa_addr.cast::<u8>(), (&mut a as *mut InetAddress).cast::<u8>(), size_of::<osdep::sockaddr_in>());
} else if sa_family == osdep::AF_INET6 as u8 {
copy_nonoverlapping((*i).ifa_addr.cast::<u8>(), (&mut a as *mut InetAddress).cast::<u8>(), size_of::<osdep::sockaddr_in6>());
} else {
continue;
}
let mut netmask_bits: u16 = 0;
if !(*i).ifa_netmask.is_null() {
if sa_family == osdep::AF_INET as u8 {
let mut a = (*(*i).ifa_netmask.cast::<osdep::sockaddr_in>()).sin_addr.s_addr as u32;
netmask_bits = a.leading_ones() as u16;
} else if sa_family == osdep::AF_INET6 as u8 {
let a = s6_addr_as_ptr(&((*(*i).ifa_netmask.cast::<osdep::sockaddr_in6>()).sin6_addr)).cast::<u8>();
for i in 0..16 as isize {
let mut b = *a.offset(i);
if b == 0xff {
netmask_bits += 8;
} else {
netmask_bits += b.leading_ones() as u16;
break;
}
}
}
}
a.set_port(netmask_bits);
f(PhysicalLink{
address: a,
device: if (*i).ifa_name.is_null() { String::new() } else { String::from(CStr::from_ptr((*i).ifa_name).to_str().unwrap()) }
});
}
i = (*i).ifa_next;
}
osdep::freeifaddrs(ifap.cast());
}
}
}
}

View file

@ -27,7 +27,7 @@ pub struct Store {
impl Store {
const MAX_OBJECT_SIZE: usize = 131072; // sanity limit
pub fn new(base_path: &str) -> Result<Store, std::io::Error> {
pub fn new(base_path: &str) -> std::io::Result<Store> {
let bp = Path::new(base_path);
let md = bp.metadata()?;
if !md.is_dir() || md.permissions().readonly() {

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
/****/
use std::collections::BTreeSet;
use std::ptr::null_mut;
use std::os::raw::c_ulong;
use zerotier_core::{MAC, MulticastGroup};
use crate::osdep as osdep;
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "ios"))]
pub(crate) fn bsd_get_multicast_groups(dev: &str) -> BTreeSet<MulticastGroup> {
let dev = dev.as_bytes();
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
unsafe {
let mut maddrs: *mut osdep::ifmaddrs = null_mut();
if osdep::getifmaddrs(&mut maddrs as *mut *mut osdep::ifmaddrs) == 0 {
let mut i = maddrs;
while !i.is_null() {
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family == osdep::AF_LINK as osdep::sa_family_t {
let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast());
let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast());
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len() as osdep::u_char && osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen as c_ulong) == 0 {
let mi = la.sdl_nlen as usize;
groups.insert(MulticastGroup{
mac: MAC(
(la.sdl_data[mi] as u64) << 40 |
(la.sdl_data[mi+1] as u64) << 32 |
(la.sdl_data[mi+2] as u64) << 24 |
(la.sdl_data[mi+3] as u64) << 16 |
(la.sdl_data[mi+4] as u64) << 8 |
la.sdl_data[mi+5] as u64
),
adi: 0,
});
}
}
i = (*i).ifma_next;
}
osdep::freeifmaddrs(maddrs);
}
}
groups
}

View file

@ -1,3 +1,16 @@
/*
* 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.
*/
/****/
/*
* This creates a pair of feth devices with the lower numbered device
* being the ZeroTier virtual interface and the higher being the device
@ -28,7 +41,7 @@ use std::error::Error;
use std::ffi::CString;
use std::ptr::{null_mut, copy_nonoverlapping};
use std::mem::{transmute, zeroed};
use std::os::raw::{c_int, c_uchar, c_ulong, c_void};
use std::os::raw::{c_int, c_uchar, c_void};
use std::process::Command;
use std::sync::Mutex;
use std::thread::JoinHandle;
@ -39,7 +52,7 @@ use num_traits::cast::AsPrimitive;
use zerotier_core::{InetAddress, MAC, MulticastGroup, NetworkId};
use crate::osdep as osdep;
use crate::physicallink::PhysicalLink;
use crate::getifaddrs;
use crate::vnic::VNIC;
const BPF_BUFFER_SIZE: usize = 131072;
@ -154,8 +167,8 @@ impl MacFethTap {
device_name = format!("feth{}", device_feth_no);
peer_device_name = format!("feth{}", device_feth_no + 5000);
let mut already_allocated = false;
PhysicalLink::map(|link: PhysicalLink| {
if link.device.eq(&device_name) || link.device.eq(&peer_device_name) {
getifaddrs::for_each_address(|_: &InetAddress, dn: &str| {
if dn.eq(&device_name) || dn.eq(&peer_device_name) {
already_allocated = true;
}
});
@ -164,7 +177,7 @@ impl MacFethTap {
}
device_alloc_tries += 1;
if device_alloc_tries >= 4899 {
if device_alloc_tries >= 1000 {
return Err(String::from("unable to find unallocated 'feth' device"));
}
device_feth_ctr += 1;
@ -394,60 +407,16 @@ impl VNIC for MacFethTap {
self.device.name.clone()
}
#[inline(always)]
fn get_multicast_groups(&self) -> BTreeSet<MulticastGroup> {
let dev = self.device.name.as_bytes();
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
unsafe {
let mut maddrs: *mut osdep::ifmaddrs = null_mut();
if osdep::getifmaddrs(&mut maddrs as *mut *mut osdep::ifmaddrs) == 0 {
let mut i = maddrs;
while !i.is_null() {
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family == osdep::AF_LINK as osdep::sa_family_t {
let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast());
let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast());
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len() as osdep::u_char && osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen as c_ulong) == 0 {
let mi = la.sdl_nlen as usize;
groups.insert(MulticastGroup{
mac: MAC(
(la.sdl_data[mi] as u64) << 40 |
(la.sdl_data[mi+1] as u64) << 32 |
(la.sdl_data[mi+2] as u64) << 24 |
(la.sdl_data[mi+3] as u64) << 16 |
(la.sdl_data[mi+4] as u64) << 8 |
la.sdl_data[mi+5] as u64
),
adi: 0,
});
}
}
i = (*i).ifma_next;
}
osdep::freeifmaddrs(maddrs);
}
}
groups
crate::vnic::common::bsd_get_multicast_groups(self.device.name.as_str())
}
#[inline(always)]
fn put(&self, source_mac: &zerotier_core::MAC, dest_mac: &zerotier_core::MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool {
let dm = dest_mac.0;
let sm = source_mac.0;
let mut hdr: [u8; 14] = [
(dm >> 40) as u8,
(dm >> 32) as u8,
(dm >> 24) as u8,
(dm >> 16) as u8,
(dm >> 8) as u8,
dm as u8,
(sm >> 40) as u8,
(sm >> 32) as u8,
(sm >> 24) as u8,
(sm >> 16) as u8,
(sm >> 8) as u8,
sm as u8,
(ethertype >> 8) as u8,
ethertype as u8
];
let mut hdr: [u8; 14] = [(dm >> 40) as u8, (dm >> 32) as u8, (dm >> 24) as u8, (dm >> 16) as u8, (dm >> 8) as u8, dm as u8, (sm >> 40) as u8, (sm >> 32) as u8, (sm >> 24) as u8, (sm >> 16) as u8, (sm >> 8) as u8, sm as u8, (ethertype >> 8) as u8, ethertype as u8];
unsafe {
let iov: [osdep::iovec; 2] = [
osdep::iovec {

View file

@ -1,11 +1,20 @@
/*
* 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.
*/
/****/
mod vnic;
mod common;
#[cfg(target_os = "macos")]
mod mac_feth_tap;
pub trait VNIC {
fn add_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
fn remove_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
fn ips(&self) -> Vec<zerotier_core::InetAddress>;
fn device_name(&self) -> String;
fn get_multicast_groups(&self) -> std::collections::BTreeSet<zerotier_core::MulticastGroup>;
fn put(&self, source_mac: &zerotier_core::MAC, dest_mac: &zerotier_core::MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool;
}
pub use vnic::VNIC;

View file

@ -0,0 +1,21 @@
/*
* 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.
*/
/****/
pub trait VNIC {
fn add_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
fn remove_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
fn ips(&self) -> Vec<zerotier_core::InetAddress>;
fn device_name(&self) -> String;
fn get_multicast_groups(&self) -> std::collections::BTreeSet<zerotier_core::MulticastGroup>;
fn put(&self, source_mac: &zerotier_core::MAC, dest_mac: &zerotier_core::MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool;
}