mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 20:13:44 +02:00
Rust service work in progress...
This commit is contained in:
parent
c29a64602a
commit
f9deec7872
33 changed files with 598 additions and 1110 deletions
|
@ -25,8 +25,6 @@
|
||||||
#include "Containers.hpp"
|
#include "Containers.hpp"
|
||||||
|
|
||||||
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
|
#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_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_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)
|
#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)
|
||||||
|
|
|
@ -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
|
|
|
@ -8,10 +8,10 @@ set(src
|
||||||
)
|
)
|
||||||
|
|
||||||
set(headers
|
set(headers
|
||||||
BlockingQueue.hpp
|
|
||||||
ManagedRoute.hpp
|
ManagedRoute.hpp
|
||||||
OSUtils.hpp
|
OSUtils.hpp
|
||||||
Thread.hpp
|
Thread.hpp
|
||||||
|
rust-osdep.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
@ -27,11 +27,6 @@ elseif(UNIX)
|
||||||
set(src ${src} freebsd_getifmaddrs.c)
|
set(src ${src} freebsd_getifmaddrs.c)
|
||||||
set(headers ${headers} freebsd_getifmaddrs.h)
|
set(headers ${headers} freebsd_getifmaddrs.h)
|
||||||
endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
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)
|
endif(WIN32)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} STATIC ${src} ${headers})
|
add_library(${PROJECT_NAME} STATIC ${src} ${headers})
|
||||||
|
|
|
@ -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__
|
|
|
@ -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
|
|
|
@ -12,7 +12,6 @@
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
#include "../core/Constants.hpp"
|
#include "../core/Constants.hpp"
|
||||||
#include "../core/Utils.hpp"
|
|
||||||
#include "../core/Containers.hpp"
|
#include "../core/Containers.hpp"
|
||||||
#include "OSUtils.hpp"
|
#include "OSUtils.hpp"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
#include "../core/Constants.hpp"
|
#include "../core/Constants.hpp"
|
||||||
|
#include "../core/Mutex.hpp"
|
||||||
|
#include "../core/Containers.hpp"
|
||||||
|
#include "OSUtils.hpp"
|
||||||
|
|
||||||
#include "rust-osdep.h"
|
#include "rust-osdep.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -13,16 +17,91 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
extern const unsigned long c_BIOCSBLEN = BIOCSBLEN;
|
const unsigned long c_BIOCSBLEN = BIOCSBLEN;
|
||||||
extern const unsigned long c_BIOCIMMEDIATE = BIOCIMMEDIATE;
|
const unsigned long c_BIOCIMMEDIATE = BIOCIMMEDIATE;
|
||||||
extern const unsigned long c_BIOCSSEESENT = BIOCSSEESENT;
|
const unsigned long c_BIOCSSEESENT = BIOCSSEESENT;
|
||||||
extern const unsigned long c_BIOCSETIF = BIOCSETIF;
|
const unsigned long c_BIOCSETIF = BIOCSETIF;
|
||||||
extern const unsigned long c_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
|
const unsigned long c_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
|
||||||
extern const unsigned long c_BIOCPROMISC = BIOCPROMISC;
|
const unsigned long c_BIOCPROMISC = BIOCPROMISC;
|
||||||
extern const unsigned long c_SIOCGIFINFO_IN6 = SIOCGIFINFO_IN6;
|
const unsigned long c_SIOCGIFINFO_IN6 = SIOCGIFINFO_IN6;
|
||||||
extern const unsigned long c_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
|
const unsigned long c_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
|
||||||
extern const unsigned long c_SIOCAUTOCONF_START = SIOCAUTOCONF_START;
|
const unsigned long c_SIOCAUTOCONF_START = SIOCAUTOCONF_START;
|
||||||
extern const unsigned long c_SIOCAUTOCONF_STOP = SIOCAUTOCONF_STOP;
|
const unsigned long c_SIOCAUTOCONF_STOP = SIOCAUTOCONF_STOP;
|
||||||
#endif
|
#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(); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,3 +46,14 @@ extern const unsigned long c_SIOCAUTOCONF_STOP;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const char *platformDefaultHomePath();
|
||||||
|
extern int64_t msSinceEpoch();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let d = env!("CARGO_MANIFEST_DIR");
|
let d = env!("CARGO_MANIFEST_DIR");
|
||||||
println!("cargo:rustc-link-search=native={}/../build/core", d);
|
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_core");
|
||||||
|
println!("cargo:rustc-link-lib=static=zt_osdep");
|
||||||
println!("cargo:rustc-link-lib=c++");
|
println!("cargo:rustc-link-lib=c++");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 std::str::FromStr;
|
||||||
use clap::{App, Arg, ArgMatches, ErrorKind};
|
use clap::{App, Arg, ArgMatches, ErrorKind};
|
||||||
|
|
||||||
|
|
0
rust-zerotier-service/src/commands/cert.rs
Normal file
0
rust-zerotier-service/src/commands/cert.rs
Normal file
0
rust-zerotier-service/src/commands/controller.rs
Normal file
0
rust-zerotier-service/src/commands/controller.rs
Normal file
0
rust-zerotier-service/src/commands/identity.rs
Normal file
0
rust-zerotier-service/src/commands/identity.rs
Normal file
0
rust-zerotier-service/src/commands/join.rs
Normal file
0
rust-zerotier-service/src/commands/join.rs
Normal file
0
rust-zerotier-service/src/commands/leave.rs
Normal file
0
rust-zerotier-service/src/commands/leave.rs
Normal file
0
rust-zerotier-service/src/commands/locator.rs
Normal file
0
rust-zerotier-service/src/commands/locator.rs
Normal file
24
rust-zerotier-service/src/commands/mod.rs
Normal file
24
rust-zerotier-service/src/commands/mod.rs
Normal 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;
|
0
rust-zerotier-service/src/commands/network.rs
Normal file
0
rust-zerotier-service/src/commands/network.rs
Normal file
0
rust-zerotier-service/src/commands/peer.rs
Normal file
0
rust-zerotier-service/src/commands/peer.rs
Normal file
206
rust-zerotier-service/src/commands/service.rs
Normal file
206
rust-zerotier-service/src/commands/service.rs
Normal 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
|
||||||
|
}
|
0
rust-zerotier-service/src/commands/set.rs
Normal file
0
rust-zerotier-service/src/commands/set.rs
Normal file
0
rust-zerotier-service/src/commands/status.rs
Normal file
0
rust-zerotier-service/src/commands/status.rs
Normal file
|
@ -173,6 +173,8 @@ impl FastUDPSocket {
|
||||||
sockets: Vec::new(),
|
sockets: Vec::new(),
|
||||||
bind_address: address.clone()
|
bind_address: address.clone()
|
||||||
};
|
};
|
||||||
|
s.threads.reserve(thread_count);
|
||||||
|
s.sockets.reserve(thread_count);
|
||||||
|
|
||||||
let mut bind_failed_reason: &'static str = "";
|
let mut bind_failed_reason: &'static str = "";
|
||||||
for _ in 0..thread_count {
|
for _ in 0..thread_count {
|
||||||
|
@ -211,6 +213,12 @@ impl FastUDPSocket {
|
||||||
Ok(s)
|
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.
|
/// Send from this socket.
|
||||||
/// This actually picks a thread's socket and sends from it. Since all
|
/// 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.
|
/// are bound to the same IP:port which one is chosen doesn't matter.
|
||||||
|
|
76
rust-zerotier-service/src/getifaddrs.rs
Normal file
76
rust-zerotier-service/src/getifaddrs.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -220,7 +220,8 @@ impl LocalConfig {
|
||||||
if md.is_err() {
|
if md.is_err() {
|
||||||
return Err(md.err().unwrap());
|
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);
|
let json = std::fs::read_to_string(path);
|
||||||
if json.is_err() {
|
if json.is_err() {
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
/****/
|
/****/
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
mod commands;
|
||||||
mod fastudpsocket;
|
mod fastudpsocket;
|
||||||
mod localconfig;
|
mod localconfig;
|
||||||
mod physicallink;
|
mod getifaddrs;
|
||||||
mod log;
|
mod log;
|
||||||
mod store;
|
mod store;
|
||||||
mod network;
|
mod network;
|
||||||
|
@ -23,205 +24,51 @@ mod vnic;
|
||||||
#[allow(non_snake_case,non_upper_case_globals,non_camel_case_types,dead_code,improper_ctypes)]
|
#[allow(non_snake_case,non_upper_case_globals,non_camel_case_types,dead_code,improper_ctypes)]
|
||||||
mod osdep;
|
mod osdep;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::boxed::Box;
|
||||||
use std::collections::BTreeMap;
|
use std::ffi::CStr;
|
||||||
use std::net::IpAddr;
|
use std::path::Path;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
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;
|
let mut process_exit_value: i32 = 0;
|
||||||
|
|
||||||
// Current active local configuration for this node.
|
let mut zerotier_path;
|
||||||
let mut local_config: Box<LocalConfig> = Box::new(LocalConfig::default());
|
unsafe {
|
||||||
|
zerotier_path = zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), 256);
|
||||||
|
}
|
||||||
|
|
||||||
// Handler for incoming packets from FastUDPSocket and incoming events from Node.
|
let mut cli_args = Some(Box::new(cli::parse_cli_args()));
|
||||||
let handler: Arc<ServiceEventHandler> = Arc::new(ServiceEventHandler{});
|
|
||||||
|
|
||||||
// From this point on we are in Tokio async land...
|
let json_output;
|
||||||
let tokio_rt = tokio::runtime::Builder::new_multi_thread().thread_stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).build().unwrap();
|
let mut token: Option<String> = None;
|
||||||
tokio_rt.block_on(async {
|
let mut token_path = Path::new(&zerotier_path).join("authtoken.secret");
|
||||||
// Keeps track of FastUDPSocket instances by bound address.
|
{
|
||||||
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
|
let a = cli_args.unwrap();
|
||||||
|
json_output = a.is_present("json");
|
||||||
// Send something to interrupt_tx to interrupt the inner loop and force it to
|
let v = a.value_of("path");
|
||||||
// detect a change or exit if run has been set to false.
|
if v.is_some() {
|
||||||
let (mut interrupt_tx, mut interrupt_rx) = futures::channel::mpsc::channel::<u8>(2);
|
zerotier_path = String::from(v.unwrap());
|
||||||
|
|
||||||
// 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 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);
|
std::process::exit(process_exit_value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {}
|
pub struct Network {}
|
||||||
|
|
||||||
impl Network {
|
impl Network {
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,7 +27,7 @@ pub struct Store {
|
||||||
impl Store {
|
impl Store {
|
||||||
const MAX_OBJECT_SIZE: usize = 131072; // sanity limit
|
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 bp = Path::new(base_path);
|
||||||
let md = bp.metadata()?;
|
let md = bp.metadata()?;
|
||||||
if !md.is_dir() || md.permissions().readonly() {
|
if !md.is_dir() || md.permissions().readonly() {
|
||||||
|
|
53
rust-zerotier-service/src/vnic/common.rs
Normal file
53
rust-zerotier-service/src/vnic/common.rs
Normal 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
|
||||||
|
}
|
|
@ -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
|
* This creates a pair of feth devices with the lower numbered device
|
||||||
* being the ZeroTier virtual interface and the higher being the 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::ffi::CString;
|
||||||
use std::ptr::{null_mut, copy_nonoverlapping};
|
use std::ptr::{null_mut, copy_nonoverlapping};
|
||||||
use std::mem::{transmute, zeroed};
|
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::process::Command;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
|
@ -39,7 +52,7 @@ use num_traits::cast::AsPrimitive;
|
||||||
use zerotier_core::{InetAddress, MAC, MulticastGroup, NetworkId};
|
use zerotier_core::{InetAddress, MAC, MulticastGroup, NetworkId};
|
||||||
|
|
||||||
use crate::osdep as osdep;
|
use crate::osdep as osdep;
|
||||||
use crate::physicallink::PhysicalLink;
|
use crate::getifaddrs;
|
||||||
use crate::vnic::VNIC;
|
use crate::vnic::VNIC;
|
||||||
|
|
||||||
const BPF_BUFFER_SIZE: usize = 131072;
|
const BPF_BUFFER_SIZE: usize = 131072;
|
||||||
|
@ -154,8 +167,8 @@ impl MacFethTap {
|
||||||
device_name = format!("feth{}", device_feth_no);
|
device_name = format!("feth{}", device_feth_no);
|
||||||
peer_device_name = format!("feth{}", device_feth_no + 5000);
|
peer_device_name = format!("feth{}", device_feth_no + 5000);
|
||||||
let mut already_allocated = false;
|
let mut already_allocated = false;
|
||||||
PhysicalLink::map(|link: PhysicalLink| {
|
getifaddrs::for_each_address(|_: &InetAddress, dn: &str| {
|
||||||
if link.device.eq(&device_name) || link.device.eq(&peer_device_name) {
|
if dn.eq(&device_name) || dn.eq(&peer_device_name) {
|
||||||
already_allocated = true;
|
already_allocated = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -164,7 +177,7 @@ impl MacFethTap {
|
||||||
}
|
}
|
||||||
|
|
||||||
device_alloc_tries += 1;
|
device_alloc_tries += 1;
|
||||||
if device_alloc_tries >= 4899 {
|
if device_alloc_tries >= 1000 {
|
||||||
return Err(String::from("unable to find unallocated 'feth' device"));
|
return Err(String::from("unable to find unallocated 'feth' device"));
|
||||||
}
|
}
|
||||||
device_feth_ctr += 1;
|
device_feth_ctr += 1;
|
||||||
|
@ -394,60 +407,16 @@ impl VNIC for MacFethTap {
|
||||||
self.device.name.clone()
|
self.device.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn get_multicast_groups(&self) -> BTreeSet<MulticastGroup> {
|
fn get_multicast_groups(&self) -> BTreeSet<MulticastGroup> {
|
||||||
let dev = self.device.name.as_bytes();
|
crate::vnic::common::bsd_get_multicast_groups(self.device.name.as_str())
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[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 {
|
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 dm = dest_mac.0;
|
||||||
let sm = source_mac.0;
|
let sm = source_mac.0;
|
||||||
let mut hdr: [u8; 14] = [
|
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];
|
||||||
(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 {
|
unsafe {
|
||||||
let iov: [osdep::iovec; 2] = [
|
let iov: [osdep::iovec; 2] = [
|
||||||
osdep::iovec {
|
osdep::iovec {
|
||||||
|
|
|
@ -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")]
|
#[cfg(target_os = "macos")]
|
||||||
mod mac_feth_tap;
|
mod mac_feth_tap;
|
||||||
|
|
||||||
pub trait VNIC {
|
pub use vnic::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;
|
|
||||||
}
|
|
||||||
|
|
21
rust-zerotier-service/src/vnic/vnic.rs
Normal file
21
rust-zerotier-service/src/vnic/vnic.rs
Normal 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;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue