mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-03 19:13:43 +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"
|
||||
|
||||
#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)
|
||||
|
|
|
@ -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
|
||||
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})
|
||||
|
|
|
@ -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/Utils.hpp"
|
||||
#include "../core/Containers.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++");
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
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(),
|
||||
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.
|
||||
|
|
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() {
|
||||
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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
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() {
|
||||
|
|
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
|
||||
* 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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
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