mirror of
https://github.com/NAStools/zerotierone.git
synced 2025-08-14 18:49:24 -07:00
Imported Upstream version 1.1.14
This commit is contained in:
commit
bb232b9d52
557 changed files with 115164 additions and 0 deletions
126
osdep/Arp.cpp
Normal file
126
osdep/Arp.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Arp.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static const uint8_t ARP_REQUEST_HEADER[8] = { 0x00,0x01,0x08,0x00,0x06,0x04,0x00,0x01 };
|
||||
static const uint8_t ARP_RESPONSE_HEADER[8] = { 0x00,0x01,0x08,0x00,0x06,0x04,0x00,0x02 };
|
||||
|
||||
Arp::Arp() :
|
||||
_cache(256),
|
||||
_lastCleaned(OSUtils::now())
|
||||
{
|
||||
}
|
||||
|
||||
void Arp::addLocal(uint32_t ip,const MAC &mac)
|
||||
{
|
||||
_ArpEntry &e = _cache[ip];
|
||||
e.lastQuerySent = 0; // local IP
|
||||
e.lastResponseReceived = 0; // local IP
|
||||
e.mac = mac;
|
||||
e.local = true;
|
||||
}
|
||||
|
||||
void Arp::remove(uint32_t ip)
|
||||
{
|
||||
_cache.erase(ip);
|
||||
}
|
||||
|
||||
uint32_t Arp::processIncomingArp(const void *arp,unsigned int len,void *response,unsigned int &responseLen,MAC &responseDest)
|
||||
{
|
||||
const uint64_t now = OSUtils::now();
|
||||
uint32_t ip = 0;
|
||||
|
||||
responseLen = 0;
|
||||
responseDest.zero();
|
||||
|
||||
if (len >= 28) {
|
||||
if (!memcmp(arp,ARP_REQUEST_HEADER,8)) {
|
||||
// Respond to ARP requests for locally-known IPs
|
||||
_ArpEntry *targetEntry = _cache.get(reinterpret_cast<const uint32_t *>(arp)[6]);
|
||||
if ((targetEntry)&&(targetEntry->local)) {
|
||||
memcpy(response,ARP_RESPONSE_HEADER,8);
|
||||
targetEntry->mac.copyTo(reinterpret_cast<uint8_t *>(response) + 8,6);
|
||||
memcpy(reinterpret_cast<uint8_t *>(response) + 14,reinterpret_cast<const uint8_t *>(arp) + 24,4);
|
||||
memcpy(reinterpret_cast<uint8_t *>(response) + 18,reinterpret_cast<const uint8_t *>(arp) + 8,10);
|
||||
responseLen = 28;
|
||||
responseDest.setTo(reinterpret_cast<const uint8_t *>(arp) + 8,6);
|
||||
}
|
||||
} else if (!memcmp(arp,ARP_RESPONSE_HEADER,8)) {
|
||||
// Learn cache entries for remote IPs from relevant ARP replies
|
||||
uint32_t responseIp = 0;
|
||||
memcpy(&responseIp,reinterpret_cast<const uint8_t *>(arp) + 14,4);
|
||||
_ArpEntry *queryEntry = _cache.get(responseIp);
|
||||
if ((queryEntry)&&(!queryEntry->local)&&((now - queryEntry->lastQuerySent) <= ZT_ARP_QUERY_MAX_TTL)) {
|
||||
queryEntry->lastResponseReceived = now;
|
||||
queryEntry->mac.setTo(reinterpret_cast<const uint8_t *>(arp) + 8,6);
|
||||
ip = responseIp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((now - _lastCleaned) >= ZT_ARP_EXPIRE) {
|
||||
_lastCleaned = now;
|
||||
Hashtable< uint32_t,_ArpEntry >::Iterator i(_cache);
|
||||
uint32_t *k = (uint32_t *)0;
|
||||
_ArpEntry *v = (_ArpEntry *)0;
|
||||
while (i.next(k,v)) {
|
||||
if ((!v->local)&&((now - v->lastResponseReceived) >= ZT_ARP_EXPIRE))
|
||||
_cache.erase(*k);
|
||||
}
|
||||
}
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
MAC Arp::query(const MAC &localMac,uint32_t localIp,uint32_t targetIp,void *query,unsigned int &queryLen,MAC &queryDest)
|
||||
{
|
||||
const uint64_t now = OSUtils::now();
|
||||
|
||||
_ArpEntry &e = _cache[targetIp];
|
||||
|
||||
if ( ((e.mac)&&((now - e.lastResponseReceived) >= (ZT_ARP_EXPIRE / 3))) ||
|
||||
((!e.mac)&&((now - e.lastQuerySent) >= ZT_ARP_QUERY_INTERVAL)) ) {
|
||||
e.lastQuerySent = now;
|
||||
|
||||
uint8_t *q = reinterpret_cast<uint8_t *>(query);
|
||||
memcpy(q,ARP_REQUEST_HEADER,8); q += 8; // ARP request header information, always the same
|
||||
localMac.copyTo(q,6); q += 6; // sending host MAC address
|
||||
memcpy(q,&localIp,4); q += 4; // sending host IP (IP already in big-endian byte order)
|
||||
memset(q,0,6); q += 6; // sending zeros for target MAC address as thats what we want to find
|
||||
memcpy(q,&targetIp,4); // target IP address for resolution (IP already in big-endian byte order)
|
||||
queryLen = 28;
|
||||
if (e.mac)
|
||||
queryDest = e.mac; // confirmation query, send directly to address holder
|
||||
else queryDest = (uint64_t)0xffffffffffffULL; // broadcast query
|
||||
} else {
|
||||
queryLen = 0;
|
||||
queryDest.zero();
|
||||
}
|
||||
|
||||
return e.mac;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
148
osdep/Arp.hpp
Normal file
148
osdep/Arp.hpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_ARP_HPP
|
||||
#define ZT_ARP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Hashtable.hpp"
|
||||
#include "../node/MAC.hpp"
|
||||
|
||||
/**
|
||||
* Maximum possible ARP length
|
||||
*
|
||||
* ARPs are 28 bytes in length, but specify a 128 byte buffer since
|
||||
* some weird extensions we may support in the future can pad them
|
||||
* out to as long as 72 bytes.
|
||||
*/
|
||||
#define ZT_ARP_BUF_LENGTH 128
|
||||
|
||||
/**
|
||||
* Minimum permitted interval between sending ARP queries for a given IP
|
||||
*/
|
||||
#define ZT_ARP_QUERY_INTERVAL 2000
|
||||
|
||||
/**
|
||||
* Maximum time between query and response, otherwise responses are discarded to prevent poisoning
|
||||
*/
|
||||
#define ZT_ARP_QUERY_MAX_TTL 5000
|
||||
|
||||
/**
|
||||
* ARP expiration time
|
||||
*/
|
||||
#define ZT_ARP_EXPIRE 600000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* ARP cache and resolver
|
||||
*
|
||||
* To implement ARP:
|
||||
*
|
||||
* (1) Call processIncomingArp() on all ARP packets received and then always
|
||||
* check responseLen after calling. If it is non-zero, send the contents
|
||||
* of response to responseDest.
|
||||
*
|
||||
* (2) Call query() to look up IP addresses, and then check queryLen. If it
|
||||
* is non-zero, send the contents of query to queryDest (usually broadcast).
|
||||
*
|
||||
* Note that either of these functions can technically generate a response or
|
||||
* a query at any time, so their result parameters for sending ARPs should
|
||||
* always be checked.
|
||||
*
|
||||
* This class is not thread-safe and must be guarded if used in multi-threaded
|
||||
* code.
|
||||
*/
|
||||
class Arp
|
||||
{
|
||||
public:
|
||||
Arp();
|
||||
|
||||
/**
|
||||
* Set a local IP entry that we should respond to ARPs for
|
||||
*
|
||||
* @param mac Our local MAC address
|
||||
* @param ip IP in big-endian byte order (sin_addr.s_addr)
|
||||
*/
|
||||
void addLocal(uint32_t ip,const MAC &mac);
|
||||
|
||||
/**
|
||||
* Delete a local IP entry or a cached ARP entry
|
||||
*
|
||||
* @param ip IP in big-endian byte order (sin_addr.s_addr)
|
||||
*/
|
||||
void remove(uint32_t ip);
|
||||
|
||||
/**
|
||||
* Process ARP packets
|
||||
*
|
||||
* For ARP queries, a response is generated and responseLen is set to its
|
||||
* frame payload length in bytes.
|
||||
*
|
||||
* For ARP responses, the cache is populated and the IP address entry that
|
||||
* was learned is returned.
|
||||
*
|
||||
* @param arp ARP frame data
|
||||
* @param len Length of ARP frame (usually 28)
|
||||
* @param response Response buffer -- MUST be a minimum of ZT_ARP_BUF_LENGTH in size
|
||||
* @param responseLen Response length, or set to 0 if no response
|
||||
* @param responseDest Destination of response, or set to null if no response
|
||||
* @return IP address learned or 0 if no new IPs in cache
|
||||
*/
|
||||
uint32_t processIncomingArp(const void *arp,unsigned int len,void *response,unsigned int &responseLen,MAC &responseDest);
|
||||
|
||||
/**
|
||||
* Get the MAC corresponding to an IP, generating a query if needed
|
||||
*
|
||||
* This returns a MAC for a remote IP. The local MAC is returned for local
|
||||
* IPs as well. It may also generate a query if the IP is not known or the
|
||||
* entry needs to be refreshed. In this case queryLen will be set to a
|
||||
* non-zero value, so this should always be checked on return even if the
|
||||
* MAC returned is non-null.
|
||||
*
|
||||
* @param localMac Local MAC address of host interface
|
||||
* @param localIp Local IP address of host interface
|
||||
* @param targetIp IP to look up
|
||||
* @param query Buffer for generated query -- MUST be a minimum of ZT_ARP_BUF_LENGTH in size
|
||||
* @param queryLen Length of generated query, or set to 0 if no query generated
|
||||
* @param queryDest Destination of query, or set to null if no query generated
|
||||
* @return MAC or 0 if no cached entry for this IP
|
||||
*/
|
||||
MAC query(const MAC &localMac,uint32_t localIp,uint32_t targetIp,void *query,unsigned int &queryLen,MAC &queryDest);
|
||||
|
||||
private:
|
||||
struct _ArpEntry
|
||||
{
|
||||
_ArpEntry() : lastQuerySent(0),lastResponseReceived(0),mac(),local(false) {}
|
||||
uint64_t lastQuerySent; // Time last query was sent or 0 for local IP
|
||||
uint64_t lastResponseReceived; // Time of last ARP response or 0 for local IP
|
||||
MAC mac; // MAC address of device responsible for IP or null if not known yet
|
||||
bool local; // True if this is a local ARP entry
|
||||
};
|
||||
|
||||
Hashtable< uint32_t,_ArpEntry > _cache;
|
||||
uint64_t _lastCleaned;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
460
osdep/BSDEthernetTap.cpp
Normal file
460
osdep/BSDEthernetTap.cpp
Normal file
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
#include "BSDEthernetTap.hpp"
|
||||
|
||||
#define ZT_BASE32_CHARS "0123456789abcdefghijklmnopqrstuv"
|
||||
|
||||
// ff:ff:ff:ff:ff:ff with no ADI
|
||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
BSDEthernetTap::BSDEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg) :
|
||||
_handler(handler),
|
||||
_arg(arg),
|
||||
_nwid(nwid),
|
||||
_mtu(mtu),
|
||||
_metric(metric),
|
||||
_fd(0),
|
||||
_enabled(true)
|
||||
{
|
||||
static Mutex globalTapCreateLock;
|
||||
char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32];
|
||||
struct stat stattmp;
|
||||
|
||||
// On FreeBSD at least we can rename, so use nwid to generate a deterministic unique zt#### name using base32
|
||||
// As a result we don't use desiredDevice
|
||||
_dev = "zt";
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 50) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 45) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 40) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 35) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 30) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 25) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 20) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 15) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 10) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]);
|
||||
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]);
|
||||
|
||||
Mutex::Lock _gl(globalTapCreateLock);
|
||||
|
||||
if (mtu > 2800)
|
||||
throw std::runtime_error("max tap MTU is 2800");
|
||||
|
||||
// On BSD we create taps and they can have high numbers, so use ones starting
|
||||
// at 9993 to not conflict with other stuff. Then we rename it to zt<base32 of nwid>
|
||||
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
|
||||
for(int i=9993;i<(9993+128);++i) {
|
||||
Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
|
||||
Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
|
||||
if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) {
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"create",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
} else throw std::runtime_error("fork() failed");
|
||||
|
||||
if (!stat(devpath,&stattmp)) {
|
||||
cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",tmpdevname,"name",_dev.c_str(),(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
if (exitcode)
|
||||
throw std::runtime_error("ifconfig rename operation failed");
|
||||
} else throw std::runtime_error("fork() failed");
|
||||
|
||||
_fd = ::open(devpath,O_RDWR);
|
||||
if (_fd > 0)
|
||||
break;
|
||||
else throw std::runtime_error("unable to open created tap device");
|
||||
} else {
|
||||
throw std::runtime_error("cannot find /dev node for newly created tap device");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_fd <= 0)
|
||||
throw std::runtime_error("unable to open TAP device or no more devices available");
|
||||
|
||||
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
|
||||
::close(_fd);
|
||||
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
|
||||
}
|
||||
|
||||
// Configure MAC address and MTU, bring interface up
|
||||
Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
|
||||
Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
|
||||
Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
if (exitcode) {
|
||||
::close(_fd);
|
||||
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
|
||||
}
|
||||
}
|
||||
|
||||
// Set close-on-exec so that devices cannot persist if we fork/exec for update
|
||||
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
::pipe(_shutdownSignalPipe);
|
||||
|
||||
_thread = Thread::start(this);
|
||||
}
|
||||
|
||||
BSDEthernetTap::~BSDEthernetTap()
|
||||
{
|
||||
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
||||
Thread::join(_thread);
|
||||
::close(_fd);
|
||||
::close(_shutdownSignalPipe[0]);
|
||||
::close(_shutdownSignalPipe[1]);
|
||||
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
}
|
||||
}
|
||||
|
||||
void BSDEthernetTap::setEnabled(bool en)
|
||||
{
|
||||
_enabled = en;
|
||||
}
|
||||
|
||||
bool BSDEthernetTap::enabled() const
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
|
||||
{
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
|
||||
_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
waitpid(cpid,&exitcode,0);
|
||||
return (exitcode == 0);
|
||||
}
|
||||
return false; // never reached, make compiler shut up about return value
|
||||
}
|
||||
|
||||
bool BSDEthernetTap::addIp(const InetAddress &ip)
|
||||
{
|
||||
if (!ip)
|
||||
return false;
|
||||
|
||||
std::vector<InetAddress> allIps(ips());
|
||||
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end())
|
||||
return true; // IP/netmask already assigned
|
||||
|
||||
// Remove and reconfigure if address is the same but netmask is different
|
||||
for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
|
||||
if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) {
|
||||
if (___removeIp(_dev,*i))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
return (exitcode == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BSDEthernetTap::removeIp(const InetAddress &ip)
|
||||
{
|
||||
if (!ip)
|
||||
return false;
|
||||
std::vector<InetAddress> allIps(ips());
|
||||
if (std::find(allIps.begin(),allIps.end(),ip) != allIps.end()) {
|
||||
if (___removeIp(_dev,ip))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> BSDEthernetTap::ips() const
|
||||
{
|
||||
struct ifaddrs *ifa = (struct ifaddrs *)0;
|
||||
if (getifaddrs(&ifa))
|
||||
return std::vector<InetAddress>();
|
||||
|
||||
std::vector<InetAddress> r;
|
||||
|
||||
struct ifaddrs *p = ifa;
|
||||
while (p) {
|
||||
if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) {
|
||||
switch(p->ifa_addr->sa_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
|
||||
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask;
|
||||
r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr)));
|
||||
} break;
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr;
|
||||
struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask;
|
||||
uint32_t b[4];
|
||||
memcpy(b,nm->sin6_addr.s6_addr,sizeof(b));
|
||||
r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3])));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
p = p->ifa_next;
|
||||
}
|
||||
|
||||
if (ifa)
|
||||
freeifaddrs(ifa);
|
||||
|
||||
std::sort(r.begin(),r.end());
|
||||
std::unique(r.begin(),r.end());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
||||
{
|
||||
char putBuf[4096];
|
||||
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
|
||||
to.copyTo(putBuf,6);
|
||||
from.copyTo(putBuf + 6,6);
|
||||
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
|
||||
memcpy(putBuf + 14,data,len);
|
||||
len += 14;
|
||||
::write(_fd,putBuf,len);
|
||||
}
|
||||
}
|
||||
|
||||
std::string BSDEthernetTap::deviceName() const
|
||||
{
|
||||
return _dev;
|
||||
}
|
||||
|
||||
void BSDEthernetTap::setFriendlyName(const char *friendlyName)
|
||||
{
|
||||
}
|
||||
|
||||
void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
|
||||
{
|
||||
std::vector<MulticastGroup> newGroups;
|
||||
|
||||
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
|
||||
if (!getifmaddrs(&ifmap)) {
|
||||
struct ifmaddrs *p = ifmap;
|
||||
while (p) {
|
||||
if (p->ifma_addr->sa_family == AF_LINK) {
|
||||
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
|
||||
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
|
||||
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
|
||||
newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
|
||||
}
|
||||
p = p->ifma_next;
|
||||
}
|
||||
freeifmaddrs(ifmap);
|
||||
}
|
||||
|
||||
std::vector<InetAddress> allIps(ips());
|
||||
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
||||
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
|
||||
|
||||
std::sort(newGroups.begin(),newGroups.end());
|
||||
std::unique(newGroups.begin(),newGroups.end());
|
||||
|
||||
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
|
||||
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
|
||||
added.push_back(*m);
|
||||
}
|
||||
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
|
||||
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
|
||||
removed.push_back(*m);
|
||||
}
|
||||
|
||||
_multicastGroups.swap(newGroups);
|
||||
}
|
||||
|
||||
/*
|
||||
bool BSDEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
||||
{
|
||||
std::set<MulticastGroup> newGroups;
|
||||
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
|
||||
if (!getifmaddrs(&ifmap)) {
|
||||
struct ifmaddrs *p = ifmap;
|
||||
while (p) {
|
||||
if (p->ifma_addr->sa_family == AF_LINK) {
|
||||
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
|
||||
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
|
||||
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
|
||||
newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
|
||||
}
|
||||
p = p->ifma_next;
|
||||
}
|
||||
freeifmaddrs(ifmap);
|
||||
}
|
||||
|
||||
{
|
||||
std::set<InetAddress> allIps(ips());
|
||||
for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i)
|
||||
newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
|
||||
if (!groups.count(*mg)) {
|
||||
groups.insert(*mg);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
|
||||
if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) {
|
||||
groups.erase(mg++);
|
||||
changed = true;
|
||||
} else ++mg;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
*/
|
||||
|
||||
void BSDEthernetTap::threadMain()
|
||||
throw()
|
||||
{
|
||||
fd_set readfds,nullfds;
|
||||
MAC to,from;
|
||||
int n,nfds,r;
|
||||
char getBuf[8194];
|
||||
|
||||
// Wait for a moment after startup -- wait for Network to finish
|
||||
// constructing itself.
|
||||
Thread::sleep(500);
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&nullfds);
|
||||
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
|
||||
|
||||
r = 0;
|
||||
for(;;) {
|
||||
FD_SET(_shutdownSignalPipe[0],&readfds);
|
||||
FD_SET(_fd,&readfds);
|
||||
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
|
||||
|
||||
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
|
||||
break;
|
||||
|
||||
if (FD_ISSET(_fd,&readfds)) {
|
||||
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
|
||||
if (n < 0) {
|
||||
if ((errno != EINTR)&&(errno != ETIMEDOUT))
|
||||
break;
|
||||
} else {
|
||||
// Some tap drivers like to send the ethernet frame and the
|
||||
// payload in two chunks, so handle that by accumulating
|
||||
// data until we have at least a frame.
|
||||
r += n;
|
||||
if (r > 14) {
|
||||
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
|
||||
r = _mtu + 14;
|
||||
|
||||
if (_enabled) {
|
||||
to.setTo(getBuf,6);
|
||||
from.setTo(getBuf + 6,6);
|
||||
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
|
||||
// TODO: VLAN support
|
||||
_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
80
osdep/BSDEthernetTap.hpp
Normal file
80
osdep/BSDEthernetTap.hpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_BSDETHERNETTAP_HPP
|
||||
#define ZT_BSDETHERNETTAP_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/MulticastGroup.hpp"
|
||||
#include "../node/MAC.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class BSDEthernetTap
|
||||
{
|
||||
public:
|
||||
BSDEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg);
|
||||
|
||||
~BSDEthernetTap();
|
||||
|
||||
void setEnabled(bool en);
|
||||
bool enabled() const;
|
||||
bool addIp(const InetAddress &ip);
|
||||
bool removeIp(const InetAddress &ip);
|
||||
std::vector<InetAddress> ips() const;
|
||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||
std::string deviceName() const;
|
||||
void setFriendlyName(const char *friendlyName);
|
||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
||||
|
||||
void threadMain()
|
||||
throw();
|
||||
|
||||
private:
|
||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
||||
void *_arg;
|
||||
uint64_t _nwid;
|
||||
Thread _thread;
|
||||
std::string _dev;
|
||||
std::vector<MulticastGroup> _multicastGroups;
|
||||
unsigned int _mtu;
|
||||
unsigned int _metric;
|
||||
int _fd;
|
||||
int _shutdownSignalPipe[2];
|
||||
volatile bool _enabled;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
121
osdep/BackgroundResolver.cpp
Normal file
121
osdep/BackgroundResolver.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "OSUtils.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "BackgroundResolver.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/*
|
||||
* We can't actually abort a job. This is a legacy characteristic of the
|
||||
* ancient synchronous resolver APIs. So to abort jobs, we just abandon
|
||||
* them by setting their parent to null.
|
||||
*/
|
||||
class BackgroundResolverJob
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
BackgroundResolver *volatile parent;
|
||||
Mutex lock;
|
||||
|
||||
void threadMain()
|
||||
throw()
|
||||
{
|
||||
std::vector<InetAddress> ips;
|
||||
try {
|
||||
ips = OSUtils::resolve(name.c_str());
|
||||
} catch ( ... ) {}
|
||||
{
|
||||
Mutex::Lock _l(lock);
|
||||
BackgroundResolver *p = parent;
|
||||
if (p)
|
||||
p->_postResult(ips);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
BackgroundResolver::BackgroundResolver(const char *name) :
|
||||
_name(name),
|
||||
_job((BackgroundResolverJob *)0),
|
||||
_callback(0),
|
||||
_arg((void *)0),
|
||||
_ips(),
|
||||
_lock()
|
||||
{
|
||||
}
|
||||
|
||||
BackgroundResolver::~BackgroundResolver()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
std::vector<InetAddress> BackgroundResolver::get() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _ips;
|
||||
}
|
||||
|
||||
void BackgroundResolver::resolveNow(void (*callback)(BackgroundResolver *,void *),void *arg)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
|
||||
if (_job) {
|
||||
Mutex::Lock _l2(_job->lock);
|
||||
_job->parent = (BackgroundResolver *)0;
|
||||
_job = (BackgroundResolverJob *)0;
|
||||
}
|
||||
|
||||
BackgroundResolverJob *j = new BackgroundResolverJob();
|
||||
j->name = _name;
|
||||
j->parent = this;
|
||||
|
||||
_job = j;
|
||||
_callback = callback;
|
||||
_arg = arg;
|
||||
|
||||
_jobThread = Thread::start(j);
|
||||
}
|
||||
|
||||
void BackgroundResolver::abort()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
if (_job) {
|
||||
Mutex::Lock _l2(_job->lock);
|
||||
_job->parent = (BackgroundResolver *)0;
|
||||
_job = (BackgroundResolverJob *)0;
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundResolver::_postResult(const std::vector<InetAddress> &ips)
|
||||
{
|
||||
void (*cb)(BackgroundResolver *,void *);
|
||||
void *a;
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_job = (BackgroundResolverJob *)0;
|
||||
cb = _callback;
|
||||
a = _arg;
|
||||
_ips = ips;
|
||||
}
|
||||
if (cb)
|
||||
cb(this,a);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
118
osdep/BackgroundResolver.hpp
Normal file
118
osdep/BackgroundResolver.hpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_BACKGROUNDRESOLVER_HPP
|
||||
#define ZT_BACKGROUNDRESOLVER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../node/NonCopyable.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class BackgroundResolverJob;
|
||||
|
||||
/**
|
||||
* A simple background resolver
|
||||
*/
|
||||
class BackgroundResolver : NonCopyable
|
||||
{
|
||||
friend class BackgroundResolverJob;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a new resolver
|
||||
*
|
||||
* resolveNow() must be called to actually initiate background resolution.
|
||||
*
|
||||
* @param name Name to resolve
|
||||
*/
|
||||
BackgroundResolver(const char *name);
|
||||
|
||||
~BackgroundResolver();
|
||||
|
||||
/**
|
||||
* @return Most recent resolver results or empty vector if none
|
||||
*/
|
||||
std::vector<InetAddress> get() const;
|
||||
|
||||
/**
|
||||
* Launch a background resolve job now
|
||||
*
|
||||
* If a resolve job is currently in progress, it is aborted and another
|
||||
* job is started.
|
||||
*
|
||||
* Note that jobs can't actually be aborted due to the limitations of the
|
||||
* ancient synchronous OS resolver APIs. As a result, in progress jobs
|
||||
* that are aborted are simply abandoned. Don't call this too frequently
|
||||
* or background threads might pile up.
|
||||
*
|
||||
* @param callback Callback function to receive notification or NULL if none
|
||||
* @praam arg Second argument to callback function
|
||||
*/
|
||||
void resolveNow(void (*callback)(BackgroundResolver *,void *) = 0,void *arg = 0);
|
||||
|
||||
/**
|
||||
* Abort (abandon) any current resolve jobs
|
||||
*/
|
||||
void abort();
|
||||
|
||||
/**
|
||||
* @return True if a background job is in progress
|
||||
*/
|
||||
inline bool running() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return (_job != (BackgroundResolverJob *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for pending job to complete (if any)
|
||||
*/
|
||||
inline void wait() const
|
||||
{
|
||||
Thread t;
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
if (!_job)
|
||||
return;
|
||||
t = _jobThread;
|
||||
}
|
||||
Thread::join(t);
|
||||
}
|
||||
|
||||
private:
|
||||
void _postResult(const std::vector<InetAddress> &ips);
|
||||
|
||||
std::string _name;
|
||||
BackgroundResolverJob *_job;
|
||||
Thread _jobThread;
|
||||
void (*_callback)(BackgroundResolver *,void *);
|
||||
void *_arg;
|
||||
std::vector<InetAddress> _ips;
|
||||
Mutex _lock;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
327
osdep/Binder.hpp
Normal file
327
osdep/Binder.hpp
Normal file
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_BINDER_HPP
|
||||
#define ZT_BINDER_HPP
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <ShlObj.h>
|
||||
#include <netioapi.h>
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <ifaddrs.h>
|
||||
#ifdef __LINUX__
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
|
||||
#include "../node/NonCopyable.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
|
||||
#include "Phy.hpp"
|
||||
|
||||
/**
|
||||
* Period between binder rescans/refreshes
|
||||
*
|
||||
* OneService also does this on detected restarts.
|
||||
*/
|
||||
#define ZT_BINDER_REFRESH_PERIOD 30000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Enumerates local devices and binds to all potential ZeroTier path endpoints
|
||||
*
|
||||
* This replaces binding to wildcard (0.0.0.0 and ::0) with explicit binding
|
||||
* as part of the path to default gateway support. Under the hood it uses
|
||||
* different queries on different OSes to enumerate devices, and also exposes
|
||||
* device enumeration and endpoint IP data for use elsewhere.
|
||||
*
|
||||
* On OSes that do not support local port enumeration or where this is not
|
||||
* meaningful, this degrades to binding to wildcard.
|
||||
*/
|
||||
class Binder : NonCopyable
|
||||
{
|
||||
private:
|
||||
struct _Binding
|
||||
{
|
||||
_Binding() :
|
||||
udpSock((PhySocket *)0),
|
||||
tcpListenSock((PhySocket *)0),
|
||||
address() {}
|
||||
|
||||
PhySocket *udpSock;
|
||||
PhySocket *tcpListenSock;
|
||||
InetAddress address;
|
||||
};
|
||||
|
||||
public:
|
||||
Binder() {}
|
||||
|
||||
/**
|
||||
* Close all bound ports
|
||||
*
|
||||
* This should be called on shutdown. It closes listen sockets and UDP ports
|
||||
* but not TCP connections from any TCP listen sockets.
|
||||
*
|
||||
* @param phy Physical interface
|
||||
*/
|
||||
template<typename PHY_HANDLER_TYPE>
|
||||
void closeAll(Phy<PHY_HANDLER_TYPE> &phy)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
|
||||
phy.close(i->udpSock,false);
|
||||
phy.close(i->tcpListenSock,false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan local devices and addresses and rebind TCP and UDP
|
||||
*
|
||||
* This should be called after wake from sleep, on detected network device
|
||||
* changes, on startup, or periodically (e.g. every 30-60s).
|
||||
*
|
||||
* @param phy Physical interface
|
||||
* @param port Port to bind to on all interfaces (TCP and UDP)
|
||||
* @param ignoreInterfacesByName Ignore these interfaces by name
|
||||
* @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*)
|
||||
* @param ignoreInterfacesByAddress Ignore these interfaces by address
|
||||
* @tparam PHY_HANDLER_TYPE Type for Phy<> template
|
||||
* @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method
|
||||
*/
|
||||
template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
|
||||
void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker)
|
||||
{
|
||||
std::map<InetAddress,std::string> localIfAddrs;
|
||||
PhySocket *udps;
|
||||
//PhySocket *tcps;
|
||||
Mutex::Lock _l(_lock);
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
char aabuf[32768];
|
||||
ULONG aalen = sizeof(aabuf);
|
||||
if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
|
||||
PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
|
||||
while (a) {
|
||||
PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
|
||||
while (ua) {
|
||||
InetAddress ip(ua->Address.lpSockaddr);
|
||||
if (ifChecker.shouldBindInterface("",ip)) {
|
||||
switch(ip.ipScope()) {
|
||||
default: break;
|
||||
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||
case InetAddress::IP_SCOPE_GLOBAL:
|
||||
case InetAddress::IP_SCOPE_SHARED:
|
||||
case InetAddress::IP_SCOPE_PRIVATE:
|
||||
ip.setPort(port);
|
||||
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
ua = ua->Next;
|
||||
}
|
||||
a = a->Next;
|
||||
}
|
||||
}
|
||||
|
||||
#else // not __WINDOWS__
|
||||
|
||||
struct ifaddrs *ifatbl = (struct ifaddrs *)0;
|
||||
struct ifaddrs *ifa;
|
||||
if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
|
||||
ifa = ifatbl;
|
||||
while (ifa) {
|
||||
if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
|
||||
InetAddress ip = *(ifa->ifa_addr);
|
||||
if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
|
||||
switch(ip.ipScope()) {
|
||||
default: break;
|
||||
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||
case InetAddress::IP_SCOPE_GLOBAL:
|
||||
case InetAddress::IP_SCOPE_SHARED:
|
||||
case InetAddress::IP_SCOPE_PRIVATE:
|
||||
ip.setPort(port);
|
||||
localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ifa = ifa->ifa_next;
|
||||
}
|
||||
freeifaddrs(ifatbl);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Default to binding to wildcard if we can't enumerate addresses
|
||||
if (localIfAddrs.empty()) {
|
||||
localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string()));
|
||||
localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string()));
|
||||
}
|
||||
|
||||
// Close any old bindings to anything that doesn't exist anymore
|
||||
for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) {
|
||||
if (localIfAddrs.find(bi->address) == localIfAddrs.end()) {
|
||||
phy.close(bi->udpSock,false);
|
||||
phy.close(bi->tcpListenSock,false);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<_Binding> newBindings;
|
||||
for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) {
|
||||
typename std::vector<_Binding>::const_iterator bi(_bindings.begin());
|
||||
while (bi != _bindings.end()) {
|
||||
if (bi->address == ii->first) {
|
||||
newBindings.push_back(*bi);
|
||||
break;
|
||||
}
|
||||
++bi;
|
||||
}
|
||||
|
||||
if (bi == _bindings.end()) {
|
||||
udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
|
||||
if (udps) {
|
||||
//tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
|
||||
//if (tcps) {
|
||||
#ifdef __LINUX__
|
||||
// Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
|
||||
if (ii->second.length() > 0) {
|
||||
int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
|
||||
char tmp[256];
|
||||
Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
|
||||
if (fd >= 0) {
|
||||
if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) {
|
||||
fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // __LINUX__
|
||||
newBindings.push_back(_Binding());
|
||||
newBindings.back().udpSock = udps;
|
||||
//newBindings.back().tcpListenSock = tcps;
|
||||
newBindings.back().address = ii->first;
|
||||
//} else {
|
||||
// phy.close(udps,false);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swapping pointers and then letting the old one fall out of scope is faster than copying again
|
||||
_bindings.swap(newBindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a UDP packet from the specified local interface, or all
|
||||
*
|
||||
* Unfortunately even by examining the routing table there is no ultimately
|
||||
* robust way to tell where we might reach another host that works in all
|
||||
* environments. As a result, we send packets with null (wildcard) local
|
||||
* addresses from *every* bound interface.
|
||||
*
|
||||
* These are typically initial HELLOs, path probes, etc., since normal
|
||||
* conversations will have a local endpoint address. So the cost is low and
|
||||
* if the peer is not reachable via that route then the packet will go
|
||||
* nowhere and nothing will happen.
|
||||
*
|
||||
* It will of course only send via interface bindings of the same socket
|
||||
* family. No point in sending V4 via V6 or vice versa.
|
||||
*
|
||||
* In any case on most hosts there's only one or two interfaces that we
|
||||
* will use, so none of this is particularly costly.
|
||||
*
|
||||
* @param local Local interface address or null address for 'all'
|
||||
* @param remote Remote address
|
||||
* @param data Data to send
|
||||
* @param len Length of data
|
||||
* @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only)
|
||||
*/
|
||||
template<typename PHY_HANDLER_TYPE>
|
||||
inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
if (local) {
|
||||
for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
|
||||
if (i->address == local) {
|
||||
if ((v4ttl)&&(local.ss_family == AF_INET))
|
||||
phy.setIp4UdpTtl(i->udpSock,v4ttl);
|
||||
const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
|
||||
if ((v4ttl)&&(local.ss_family == AF_INET))
|
||||
phy.setIp4UdpTtl(i->udpSock,255);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
bool result = false;
|
||||
for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
|
||||
if (i->address.ss_family == remote.ss_family) {
|
||||
if ((v4ttl)&&(remote.ss_family == AF_INET))
|
||||
phy.setIp4UdpTtl(i->udpSock,v4ttl);
|
||||
result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
|
||||
if ((v4ttl)&&(remote.ss_family == AF_INET))
|
||||
phy.setIp4UdpTtl(i->udpSock,255);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All currently bound local interface addresses
|
||||
*/
|
||||
inline std::vector<InetAddress> allBoundLocalInterfaceAddresses()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
std::vector<InetAddress> aa;
|
||||
for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i)
|
||||
aa.push_back(i->address);
|
||||
return aa;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<_Binding> _bindings;
|
||||
Mutex _lock;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
291
osdep/Http.cpp
Normal file
291
osdep/Http.cpp
Normal file
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Http.hpp"
|
||||
#include "Phy.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
|
||||
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
|
||||
#include <http_parser.h>
|
||||
#else
|
||||
#include "../ext/http-parser/http_parser.h"
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
namespace {
|
||||
|
||||
static int ShttpOnMessageBegin(http_parser *parser);
|
||||
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
|
||||
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
|
||||
static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length);
|
||||
#else
|
||||
static int ShttpOnStatus(http_parser *parser);
|
||||
#endif
|
||||
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length);
|
||||
static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length);
|
||||
static int ShttpOnHeadersComplete(http_parser *parser);
|
||||
static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length);
|
||||
static int ShttpOnMessageComplete(http_parser *parser);
|
||||
|
||||
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 1)
|
||||
static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
|
||||
ShttpOnMessageBegin,
|
||||
ShttpOnUrl,
|
||||
ShttpOnStatus,
|
||||
ShttpOnHeaderField,
|
||||
ShttpOnValue,
|
||||
ShttpOnHeadersComplete,
|
||||
ShttpOnBody,
|
||||
ShttpOnMessageComplete
|
||||
};
|
||||
#else
|
||||
static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
|
||||
ShttpOnMessageBegin,
|
||||
ShttpOnUrl,
|
||||
ShttpOnHeaderField,
|
||||
ShttpOnValue,
|
||||
ShttpOnHeadersComplete,
|
||||
ShttpOnBody,
|
||||
ShttpOnMessageComplete
|
||||
};
|
||||
#endif
|
||||
|
||||
struct HttpPhyHandler
|
||||
{
|
||||
// not used
|
||||
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) {}
|
||||
inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {}
|
||||
|
||||
inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
|
||||
{
|
||||
if (success) {
|
||||
phy->setNotifyWritable(sock,true);
|
||||
} else {
|
||||
*responseBody = "connection failed";
|
||||
error = true;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void phyOnTcpClose(PhySocket *sock,void **uptr)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
||||
inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
|
||||
{
|
||||
lastActivity = OSUtils::now();
|
||||
http_parser_execute(&parser,&HTTP_PARSER_SETTINGS,(const char *)data,len);
|
||||
if ((parser.upgrade)||(parser.http_errno != HPE_OK))
|
||||
phy->close(sock);
|
||||
}
|
||||
|
||||
inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
|
||||
{
|
||||
if (writePtr < writeSize) {
|
||||
long n = phy->streamSend(sock,writeBuf + writePtr,writeSize - writePtr,true);
|
||||
if (n > 0)
|
||||
writePtr += n;
|
||||
}
|
||||
if (writePtr >= writeSize)
|
||||
phy->setNotifyWritable(sock,false);
|
||||
}
|
||||
|
||||
inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {}
|
||||
#ifdef __UNIX_LIKE__
|
||||
inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
|
||||
inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
|
||||
inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
|
||||
inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
|
||||
#endif // __UNIX_LIKE__
|
||||
|
||||
http_parser parser;
|
||||
std::string currentHeaderField;
|
||||
std::string currentHeaderValue;
|
||||
unsigned long messageSize;
|
||||
unsigned long writePtr;
|
||||
uint64_t lastActivity;
|
||||
unsigned long writeSize;
|
||||
char writeBuf[32768];
|
||||
|
||||
unsigned long maxResponseSize;
|
||||
std::map<std::string,std::string> *responseHeaders;
|
||||
std::string *responseBody;
|
||||
bool error;
|
||||
bool done;
|
||||
|
||||
Phy<HttpPhyHandler *> *phy;
|
||||
PhySocket *sock;
|
||||
};
|
||||
|
||||
static int ShttpOnMessageBegin(http_parser *parser)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
|
||||
static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
|
||||
#else
|
||||
static int ShttpOnStatus(http_parser *parser)
|
||||
#endif
|
||||
{
|
||||
/*
|
||||
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
|
||||
hh->messageSize += (unsigned long)length;
|
||||
if (hh->messageSize > hh->maxResponseSize)
|
||||
return -1;
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
|
||||
{
|
||||
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
|
||||
hh->messageSize += (unsigned long)length;
|
||||
if (hh->messageSize > hh->maxResponseSize)
|
||||
return -1;
|
||||
if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) {
|
||||
(*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
|
||||
hh->currentHeaderField = "";
|
||||
hh->currentHeaderValue = "";
|
||||
}
|
||||
for(size_t i=0;i<length;++i)
|
||||
hh->currentHeaderField.push_back(OSUtils::toLower(ptr[i]));
|
||||
return 0;
|
||||
}
|
||||
static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length)
|
||||
{
|
||||
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
|
||||
hh->messageSize += (unsigned long)length;
|
||||
if (hh->messageSize > hh->maxResponseSize)
|
||||
return -1;
|
||||
hh->currentHeaderValue.append(ptr,length);
|
||||
return 0;
|
||||
}
|
||||
static int ShttpOnHeadersComplete(http_parser *parser)
|
||||
{
|
||||
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
|
||||
if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length()))
|
||||
(*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
|
||||
return 0;
|
||||
}
|
||||
static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
|
||||
{
|
||||
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
|
||||
hh->messageSize += (unsigned long)length;
|
||||
if (hh->messageSize > hh->maxResponseSize)
|
||||
return -1;
|
||||
hh->responseBody->append(ptr,length);
|
||||
return 0;
|
||||
}
|
||||
static int ShttpOnMessageComplete(http_parser *parser)
|
||||
{
|
||||
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
|
||||
hh->phy->close(hh->sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
unsigned int Http::_do(
|
||||
const char *method,
|
||||
unsigned long maxResponseSize,
|
||||
unsigned long timeout,
|
||||
const struct sockaddr *remoteAddress,
|
||||
const char *path,
|
||||
const std::map<std::string,std::string> &requestHeaders,
|
||||
const void *requestBody,
|
||||
unsigned long requestBodyLength,
|
||||
std::map<std::string,std::string> &responseHeaders,
|
||||
std::string &responseBody)
|
||||
{
|
||||
try {
|
||||
responseHeaders.clear();
|
||||
responseBody = "";
|
||||
|
||||
HttpPhyHandler handler;
|
||||
|
||||
http_parser_init(&(handler.parser),HTTP_RESPONSE);
|
||||
handler.parser.data = (void *)&handler;
|
||||
handler.messageSize = 0;
|
||||
handler.writePtr = 0;
|
||||
handler.lastActivity = OSUtils::now();
|
||||
|
||||
try {
|
||||
handler.writeSize = Utils::snprintf(handler.writeBuf,sizeof(handler.writeBuf),"%s %s HTTP/1.1\r\n",method,path);
|
||||
for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h)
|
||||
handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"%s: %s\r\n",h->first.c_str(),h->second.c_str());
|
||||
handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"\r\n");
|
||||
if ((requestBody)&&(requestBodyLength)) {
|
||||
if ((handler.writeSize + requestBodyLength) > sizeof(handler.writeBuf)) {
|
||||
responseBody = "request too large";
|
||||
return 0;
|
||||
}
|
||||
memcpy(handler.writeBuf + handler.writeSize,requestBody,requestBodyLength);
|
||||
handler.writeSize += requestBodyLength;
|
||||
}
|
||||
} catch ( ... ) {
|
||||
responseBody = "request too large";
|
||||
return 0;
|
||||
}
|
||||
|
||||
handler.maxResponseSize = maxResponseSize;
|
||||
handler.responseHeaders = &responseHeaders;
|
||||
handler.responseBody = &responseBody;
|
||||
handler.error = false;
|
||||
handler.done = false;
|
||||
|
||||
Phy<HttpPhyHandler *> phy(&handler,true,true);
|
||||
|
||||
bool instantConnect = false;
|
||||
handler.phy = &phy;
|
||||
handler.sock = phy.tcpConnect((const struct sockaddr *)remoteAddress,instantConnect,(void *)0,true);
|
||||
if (!handler.sock) {
|
||||
responseBody = "connection failed (2)";
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (!handler.done) {
|
||||
phy.poll(timeout / 2);
|
||||
if ((timeout)&&((unsigned long)(OSUtils::now() - handler.lastActivity) > timeout)) {
|
||||
phy.close(handler.sock);
|
||||
responseBody = "timed out";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ((handler.error) ? 0 : ((handler.parser.http_errno != HPE_OK) ? 0 : handler.parser.status_code));
|
||||
} catch (std::exception &exc) {
|
||||
responseBody = exc.what();
|
||||
return 0;
|
||||
} catch ( ... ) {
|
||||
responseBody = "unknown exception";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
154
osdep/Http.hpp
Normal file
154
osdep/Http.hpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_HTTP_HPP
|
||||
#define ZT_HTTP_HPP
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Simple synchronous HTTP client used for updater and cli
|
||||
*/
|
||||
class Http
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Make HTTP GET request
|
||||
*
|
||||
* The caller must set all headers, including Host.
|
||||
*
|
||||
* @return HTTP status code or 0 on error (responseBody will contain error message)
|
||||
*/
|
||||
static inline unsigned int GET(
|
||||
unsigned long maxResponseSize,
|
||||
unsigned long timeout,
|
||||
const struct sockaddr *remoteAddress,
|
||||
const char *path,
|
||||
const std::map<std::string,std::string> &requestHeaders,
|
||||
std::map<std::string,std::string> &responseHeaders,
|
||||
std::string &responseBody)
|
||||
{
|
||||
return _do(
|
||||
"GET",
|
||||
maxResponseSize,
|
||||
timeout,
|
||||
remoteAddress,
|
||||
path,
|
||||
requestHeaders,
|
||||
(const void *)0,
|
||||
0,
|
||||
responseHeaders,
|
||||
responseBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make HTTP DELETE request
|
||||
*
|
||||
* The caller must set all headers, including Host.
|
||||
*
|
||||
* @return HTTP status code or 0 on error (responseBody will contain error message)
|
||||
*/
|
||||
static inline unsigned int DEL(
|
||||
unsigned long maxResponseSize,
|
||||
unsigned long timeout,
|
||||
const struct sockaddr *remoteAddress,
|
||||
const char *path,
|
||||
const std::map<std::string,std::string> &requestHeaders,
|
||||
std::map<std::string,std::string> &responseHeaders,
|
||||
std::string &responseBody)
|
||||
{
|
||||
return _do(
|
||||
"DELETE",
|
||||
maxResponseSize,
|
||||
timeout,
|
||||
remoteAddress,
|
||||
path,
|
||||
requestHeaders,
|
||||
(const void *)0,
|
||||
0,
|
||||
responseHeaders,
|
||||
responseBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make HTTP POST request
|
||||
*
|
||||
* It is the responsibility of the caller to set all headers. With POST, the
|
||||
* Content-Length and Content-Type headers must be set or the POST will not
|
||||
* work.
|
||||
*
|
||||
* @return HTTP status code or 0 on error (responseBody will contain error message)
|
||||
*/
|
||||
static inline unsigned int POST(
|
||||
unsigned long maxResponseSize,
|
||||
unsigned long timeout,
|
||||
const struct sockaddr *remoteAddress,
|
||||
const char *path,
|
||||
const std::map<std::string,std::string> &requestHeaders,
|
||||
const void *postData,
|
||||
unsigned long postDataLength,
|
||||
std::map<std::string,std::string> &responseHeaders,
|
||||
std::string &responseBody)
|
||||
{
|
||||
return _do(
|
||||
"POST",
|
||||
maxResponseSize,
|
||||
timeout,
|
||||
remoteAddress,
|
||||
path,
|
||||
requestHeaders,
|
||||
postData,
|
||||
postDataLength,
|
||||
responseHeaders,
|
||||
responseBody);
|
||||
}
|
||||
|
||||
private:
|
||||
static unsigned int _do(
|
||||
const char *method,
|
||||
unsigned long maxResponseSize,
|
||||
unsigned long timeout,
|
||||
const struct sockaddr *remoteAddress,
|
||||
const char *path,
|
||||
const std::map<std::string,std::string> &requestHeaders,
|
||||
const void *requestBody,
|
||||
unsigned long requestBodyLength,
|
||||
std::map<std::string,std::string> &responseHeaders,
|
||||
std::string &responseBody);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
425
osdep/LinuxEthernetTap.cpp
Normal file
425
osdep/LinuxEthernetTap.cpp
Normal file
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 "../node/Constants.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "../node/Dictionary.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
#include "LinuxEthernetTap.hpp"
|
||||
|
||||
// ff:ff:ff:ff:ff:ff with no ADI
|
||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static Mutex __tapCreateLock;
|
||||
|
||||
LinuxEthernetTap::LinuxEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg) :
|
||||
_handler(handler),
|
||||
_arg(arg),
|
||||
_nwid(nwid),
|
||||
_homePath(homePath),
|
||||
_mtu(mtu),
|
||||
_fd(0),
|
||||
_enabled(true)
|
||||
{
|
||||
char procpath[128],nwids[32];
|
||||
struct stat sbuf;
|
||||
|
||||
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
|
||||
|
||||
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
|
||||
|
||||
if (mtu > 2800)
|
||||
throw std::runtime_error("max tap MTU is 2800");
|
||||
|
||||
_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));
|
||||
|
||||
// Try to recall our last device name, or pick an unused one if that fails.
|
||||
bool recalledDevice = false;
|
||||
std::string devmapbuf;
|
||||
Dictionary<8194> devmap;
|
||||
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
|
||||
devmap.load(devmapbuf.c_str());
|
||||
char desiredDevice[128];
|
||||
if (devmap.get(nwids,desiredDevice,sizeof(desiredDevice)) > 0) {
|
||||
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),desiredDevice);
|
||||
Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
|
||||
recalledDevice = (stat(procpath,&sbuf) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!recalledDevice) {
|
||||
int devno = 0;
|
||||
do {
|
||||
Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++);
|
||||
Utils::snprintf(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
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
_dev = ifr.ifr_name;
|
||||
|
||||
::ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here
|
||||
|
||||
// Open an arbitrary socket to talk to netlink
|
||||
int sock = socket(AF_INET,SOCK_DGRAM,0);
|
||||
if (sock <= 0) {
|
||||
::close(_fd);
|
||||
throw std::runtime_error("unable to open netlink socket");
|
||||
}
|
||||
|
||||
// Set MAC address
|
||||
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(_fd);
|
||||
::close(sock);
|
||||
throw std::runtime_error("unable to configure TAP hardware (MAC) address");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set MTU
|
||||
ifr.ifr_ifru.ifru_mtu = (int)mtu;
|
||||
if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) {
|
||||
::close(_fd);
|
||||
::close(sock);
|
||||
throw std::runtime_error("unable to configure TAP MTU");
|
||||
}
|
||||
|
||||
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
|
||||
::close(_fd);
|
||||
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
|
||||
}
|
||||
|
||||
/* Bring interface up */
|
||||
if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) {
|
||||
::close(_fd);
|
||||
::close(sock);
|
||||
throw std::runtime_error("unable to get TAP interface flags");
|
||||
}
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) {
|
||||
::close(_fd);
|
||||
::close(sock);
|
||||
throw std::runtime_error("unable to set TAP interface flags");
|
||||
}
|
||||
|
||||
::close(sock);
|
||||
|
||||
// Set close-on-exec so that devices cannot persist if we fork/exec for update
|
||||
::fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
(void)::pipe(_shutdownSignalPipe);
|
||||
|
||||
devmap.erase(nwids);
|
||||
devmap.add(nwids,_dev.c_str());
|
||||
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),(const void *)devmap.data(),devmap.sizeBytes());
|
||||
|
||||
_thread = Thread::start(this);
|
||||
}
|
||||
|
||||
LinuxEthernetTap::~LinuxEthernetTap()
|
||||
{
|
||||
(void)::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
||||
Thread::join(_thread);
|
||||
::close(_fd);
|
||||
::close(_shutdownSignalPipe[0]);
|
||||
::close(_shutdownSignalPipe[1]);
|
||||
}
|
||||
|
||||
void LinuxEthernetTap::setEnabled(bool en)
|
||||
{
|
||||
_enabled = en;
|
||||
}
|
||||
|
||||
bool LinuxEthernetTap::enabled() const
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
|
||||
{
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
||||
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
|
||||
::execlp("ip","ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
|
||||
::_exit(-1);
|
||||
} else {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
return (exitcode == 0);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
||||
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
|
||||
if (ip.isV4()) {
|
||||
::execlp("ip","ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0);
|
||||
} else {
|
||||
::execlp("ip","ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
|
||||
}
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
return (exitcode == 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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[8194];
|
||||
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::threadMain()
|
||||
throw()
|
||||
{
|
||||
fd_set readfds,nullfds;
|
||||
MAC to,from;
|
||||
int n,nfds,r;
|
||||
char getBuf[8194];
|
||||
|
||||
Thread::sleep(500);
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&nullfds);
|
||||
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
|
||||
|
||||
r = 0;
|
||||
for(;;) {
|
||||
FD_SET(_shutdownSignalPipe[0],&readfds);
|
||||
FD_SET(_fd,&readfds);
|
||||
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
|
||||
|
||||
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
|
||||
break;
|
||||
|
||||
if (FD_ISSET(_fd,&readfds)) {
|
||||
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
|
||||
if (n < 0) {
|
||||
if ((errno != EINTR)&&(errno != ETIMEDOUT))
|
||||
break;
|
||||
} else {
|
||||
// Some tap drivers like to send the ethernet frame and the
|
||||
// payload in two chunks, so handle that by accumulating
|
||||
// data until we have at least a frame.
|
||||
r += n;
|
||||
if (r > 14) {
|
||||
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
|
||||
r = _mtu + 14;
|
||||
|
||||
if (_enabled) {
|
||||
to.setTo(getBuf,6);
|
||||
from.setTo(getBuf + 6,6);
|
||||
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
|
||||
// TODO: VLAN support
|
||||
_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
81
osdep/LinuxEthernetTap.hpp
Normal file
81
osdep/LinuxEthernetTap.hpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_LINUXETHERNETTAP_HPP
|
||||
#define ZT_LINUXETHERNETTAP_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../node/MulticastGroup.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Linux Ethernet tap using kernel tun/tap driver
|
||||
*/
|
||||
class LinuxEthernetTap
|
||||
{
|
||||
public:
|
||||
LinuxEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg);
|
||||
|
||||
~LinuxEthernetTap();
|
||||
|
||||
void setEnabled(bool en);
|
||||
bool enabled() const;
|
||||
bool addIp(const InetAddress &ip);
|
||||
bool removeIp(const InetAddress &ip);
|
||||
std::vector<InetAddress> ips() const;
|
||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||
std::string deviceName() const;
|
||||
void setFriendlyName(const char *friendlyName);
|
||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
||||
|
||||
void threadMain()
|
||||
throw();
|
||||
|
||||
private:
|
||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
||||
void *_arg;
|
||||
uint64_t _nwid;
|
||||
Thread _thread;
|
||||
std::string _homePath;
|
||||
std::string _dev;
|
||||
std::vector<MulticastGroup> _multicastGroups;
|
||||
unsigned int _mtu;
|
||||
int _fd;
|
||||
int _shutdownSignalPipe[2];
|
||||
volatile bool _enabled;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
594
osdep/ManagedRoute.cpp
Normal file
594
osdep/ManagedRoute.cpp
Normal file
|
@ -0,0 +1,594 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <netioapi.h>
|
||||
#include <IPHlpApi.h>
|
||||
#endif
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if.h>
|
||||
#ifdef __BSD__
|
||||
#include <net/if_dl.h>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "ManagedRoute.hpp"
|
||||
|
||||
#define ZT_BSD_ROUTE_CMD "/sbin/route"
|
||||
#define ZT_LINUX_IP_COMMAND "/sbin/ip"
|
||||
#define ZT_LINUX_IP_COMMAND_2 "/usr/sbin/ip"
|
||||
|
||||
// NOTE: BSD is mostly tested on Apple/Mac but is likely to work on other BSD too
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
namespace {
|
||||
|
||||
// Fork a target into two more specific targets e.g. 0.0.0.0/0 -> 0.0.0.0/1, 128.0.0.0/1
|
||||
// If the target is already maximally-specific, 'right' will be unchanged and 'left' will be 't'
|
||||
static void _forkTarget(const InetAddress &t,InetAddress &left,InetAddress &right)
|
||||
{
|
||||
const unsigned int bits = t.netmaskBits() + 1;
|
||||
left = t;
|
||||
if ((t.ss_family == AF_INET)&&(bits <= 32)) {
|
||||
left.setPort(bits);
|
||||
right = t;
|
||||
reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
|
||||
right.setPort(bits);
|
||||
} else if ((t.ss_family == AF_INET6)&&(bits <= 128)) {
|
||||
left.setPort(bits);
|
||||
right = t;
|
||||
uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
|
||||
b[bits / 8] ^= 1 << (8 - (bits % 8));
|
||||
right.setPort(bits);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __BSD__ // ------------------------------------------------------------
|
||||
#define ZT_ROUTING_SUPPORT_FOUND 1
|
||||
|
||||
struct _RTE
|
||||
{
|
||||
InetAddress target;
|
||||
InetAddress via;
|
||||
char device[128];
|
||||
int metric;
|
||||
bool ifscope;
|
||||
};
|
||||
|
||||
static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
||||
{
|
||||
std::vector<_RTE> rtes;
|
||||
int mib[6];
|
||||
size_t needed;
|
||||
|
||||
mib[0] = CTL_NET;
|
||||
mib[1] = PF_ROUTE;
|
||||
mib[2] = 0;
|
||||
mib[3] = 0;
|
||||
mib[4] = NET_RT_DUMP;
|
||||
mib[5] = 0;
|
||||
if (!sysctl(mib,6,NULL,&needed,NULL,0)) {
|
||||
if (needed <= 0)
|
||||
return rtes;
|
||||
|
||||
char *buf = (char *)::malloc(needed);
|
||||
if (buf) {
|
||||
if (!sysctl(mib,6,buf,&needed,NULL,0)) {
|
||||
struct rt_msghdr *rtm;
|
||||
for(char *next=buf,*end=buf+needed;next<end;) {
|
||||
rtm = (struct rt_msghdr *)next;
|
||||
char *saptr = (char *)(rtm + 1);
|
||||
char *saend = next + rtm->rtm_msglen;
|
||||
|
||||
InetAddress sa_t,sa_v;
|
||||
int deviceIndex = -9999;
|
||||
|
||||
if (((rtm->rtm_flags & RTF_LLINFO) == 0)&&((rtm->rtm_flags & RTF_HOST) == 0)&&((rtm->rtm_flags & RTF_UP) != 0)&&((rtm->rtm_flags & RTF_MULTICAST) == 0)) {
|
||||
int which = 0;
|
||||
while (saptr < saend) {
|
||||
struct sockaddr *sa = (struct sockaddr *)saptr;
|
||||
unsigned int salen = sa->sa_len;
|
||||
if (!salen)
|
||||
break;
|
||||
|
||||
// Skip missing fields in rtm_addrs bit field
|
||||
while ((rtm->rtm_addrs & 1) == 0) {
|
||||
rtm->rtm_addrs >>= 1;
|
||||
++which;
|
||||
if (which > 6)
|
||||
break;
|
||||
}
|
||||
if (which > 6)
|
||||
break;
|
||||
|
||||
rtm->rtm_addrs >>= 1;
|
||||
switch(which++) {
|
||||
case 0:
|
||||
//printf("RTA_DST\n");
|
||||
if (sa->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
||||
if ((sin6->sin6_addr.s6_addr[0] == 0xfe)&&((sin6->sin6_addr.s6_addr[1] & 0xc0) == 0x80)) {
|
||||
// BSD uses this fucking strange in-band signaling method to encode device scope IDs for IPv6 addresses... probably a holdover from very early versions of the spec.
|
||||
unsigned int interfaceIndex = ((((unsigned int)sin6->sin6_addr.s6_addr[2]) << 8) & 0xff) | (((unsigned int)sin6->sin6_addr.s6_addr[3]) & 0xff);
|
||||
sin6->sin6_addr.s6_addr[2] = 0;
|
||||
sin6->sin6_addr.s6_addr[3] = 0;
|
||||
if (!sin6->sin6_scope_id)
|
||||
sin6->sin6_scope_id = interfaceIndex;
|
||||
}
|
||||
}
|
||||
sa_t = *sa;
|
||||
break;
|
||||
case 1:
|
||||
//printf("RTA_GATEWAY\n");
|
||||
switch(sa->sa_family) {
|
||||
case AF_LINK:
|
||||
deviceIndex = (int)((const struct sockaddr_dl *)sa)->sdl_index;
|
||||
break;
|
||||
case AF_INET:
|
||||
case AF_INET6:
|
||||
sa_v = *sa;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2: {
|
||||
//printf("RTA_NETMASK\n");
|
||||
if (sa_t.ss_family == AF_INET6) {
|
||||
salen = sizeof(struct sockaddr_in6);
|
||||
unsigned int bits = 0;
|
||||
for(int i=0;i<16;++i) {
|
||||
unsigned char c = (unsigned char)((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[i];
|
||||
if (c == 0xff)
|
||||
bits += 8;
|
||||
else break;
|
||||
}
|
||||
sa_t.setPort(bits);
|
||||
} else if (sa_t.ss_family == AF_INET) {
|
||||
salen = sizeof(struct sockaddr_in);
|
||||
sa_t.setPort((unsigned int)Utils::countBits((uint32_t)((const struct sockaddr_in *)sa)->sin_addr.s_addr));
|
||||
}
|
||||
} break;
|
||||
/*
|
||||
case 3:
|
||||
//printf("RTA_GENMASK\n");
|
||||
break;
|
||||
case 4:
|
||||
//printf("RTA_IFP\n");
|
||||
break;
|
||||
case 5:
|
||||
//printf("RTA_IFA\n");
|
||||
break;
|
||||
case 6:
|
||||
//printf("RTA_AUTHOR\n");
|
||||
break;
|
||||
*/
|
||||
}
|
||||
|
||||
saptr += salen;
|
||||
}
|
||||
|
||||
if (((contains)&&(sa_t.containsAddress(target)))||(sa_t == target)) {
|
||||
rtes.push_back(_RTE());
|
||||
rtes.back().target = sa_t;
|
||||
rtes.back().via = sa_v;
|
||||
if (deviceIndex >= 0) {
|
||||
if_indextoname(deviceIndex,rtes.back().device);
|
||||
} else {
|
||||
rtes.back().device[0] = (char)0;
|
||||
}
|
||||
rtes.back().metric = ((int)rtm->rtm_rmx.rmx_hopcount < 0) ? 0 : (int)rtm->rtm_rmx.rmx_hopcount;
|
||||
}
|
||||
}
|
||||
|
||||
next = saend;
|
||||
}
|
||||
}
|
||||
|
||||
::free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
return rtes;
|
||||
}
|
||||
|
||||
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
|
||||
{
|
||||
long p = (long)fork();
|
||||
if (p > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(p,&exitcode,0);
|
||||
} else if (p == 0) {
|
||||
::close(STDOUT_FILENO);
|
||||
::close(STDERR_FILENO);
|
||||
if (via) {
|
||||
if ((ifscope)&&(ifscope[0])) {
|
||||
::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
|
||||
} else {
|
||||
::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
|
||||
}
|
||||
} else if ((localInterface)&&(localInterface[0])) {
|
||||
if ((ifscope)&&(ifscope[0])) {
|
||||
::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
|
||||
} else {
|
||||
::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
|
||||
}
|
||||
}
|
||||
::_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __BSD__ ------------------------------------------------------------
|
||||
|
||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||
#define ZT_ROUTING_SUPPORT_FOUND 1
|
||||
|
||||
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *localInterface)
|
||||
{
|
||||
long p = (long)fork();
|
||||
if (p > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(p,&exitcode,0);
|
||||
} else if (p == 0) {
|
||||
::close(STDOUT_FILENO);
|
||||
::close(STDERR_FILENO);
|
||||
if (via) {
|
||||
::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0);
|
||||
::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0);
|
||||
} else if ((localInterface)&&(localInterface[0])) {
|
||||
::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0);
|
||||
::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0);
|
||||
}
|
||||
::_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __LINUX__ ----------------------------------------------------------
|
||||
|
||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||
#define ZT_ROUTING_SUPPORT_FOUND 1
|
||||
|
||||
static bool _winRoute(bool del,const NET_LUID &interfaceLuid,const NET_IFINDEX &interfaceIndex,const InetAddress &target,const InetAddress &via)
|
||||
{
|
||||
MIB_IPFORWARD_ROW2 rtrow;
|
||||
InitializeIpForwardEntry(&rtrow);
|
||||
rtrow.InterfaceLuid.Value = interfaceLuid.Value;
|
||||
rtrow.InterfaceIndex = interfaceIndex;
|
||||
if (target.ss_family == AF_INET) {
|
||||
rtrow.DestinationPrefix.Prefix.si_family = AF_INET;
|
||||
rtrow.DestinationPrefix.Prefix.Ipv4.sin_family = AF_INET;
|
||||
rtrow.DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr = reinterpret_cast<const struct sockaddr_in *>(&target)->sin_addr.S_un.S_addr;
|
||||
if (via.ss_family == AF_INET) {
|
||||
rtrow.NextHop.si_family = AF_INET;
|
||||
rtrow.NextHop.Ipv4.sin_family = AF_INET;
|
||||
rtrow.NextHop.Ipv4.sin_addr.S_un.S_addr = reinterpret_cast<const struct sockaddr_in *>(&via)->sin_addr.S_un.S_addr;
|
||||
}
|
||||
} else if (target.ss_family == AF_INET6) {
|
||||
rtrow.DestinationPrefix.Prefix.si_family = AF_INET6;
|
||||
rtrow.DestinationPrefix.Prefix.Ipv6.sin6_family = AF_INET6;
|
||||
memcpy(rtrow.DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte,reinterpret_cast<const struct sockaddr_in6 *>(&target)->sin6_addr.u.Byte,16);
|
||||
if (via.ss_family == AF_INET6) {
|
||||
rtrow.NextHop.si_family = AF_INET6;
|
||||
rtrow.NextHop.Ipv6.sin6_family = AF_INET6;
|
||||
memcpy(rtrow.NextHop.Ipv6.sin6_addr.u.Byte,reinterpret_cast<const struct sockaddr_in6 *>(&via)->sin6_addr.u.Byte,16);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
rtrow.DestinationPrefix.PrefixLength = target.netmaskBits();
|
||||
rtrow.SitePrefixLength = rtrow.DestinationPrefix.PrefixLength;
|
||||
rtrow.ValidLifetime = 0xffffffff;
|
||||
rtrow.PreferredLifetime = 0xffffffff;
|
||||
rtrow.Metric = -1;
|
||||
rtrow.Protocol = MIB_IPPROTO_NETMGMT;
|
||||
rtrow.Loopback = FALSE;
|
||||
rtrow.AutoconfigureAddress = FALSE;
|
||||
rtrow.Publish = FALSE;
|
||||
rtrow.Immortal = FALSE;
|
||||
rtrow.Age = 0;
|
||||
rtrow.Origin = NlroManual;
|
||||
if (del) {
|
||||
return (DeleteIpForwardEntry2(&rtrow) == NO_ERROR);
|
||||
} else {
|
||||
NTSTATUS r = CreateIpForwardEntry2(&rtrow);
|
||||
if (r == NO_ERROR) {
|
||||
return true;
|
||||
} else if (r == ERROR_OBJECT_ALREADY_EXISTS) {
|
||||
return (SetIpForwardEntry2(&rtrow) == NO_ERROR);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ --------------------------------------------------------
|
||||
|
||||
#ifndef ZT_ROUTING_SUPPORT_FOUND
|
||||
#error "ManagedRoute.cpp has no support for managing routes on this platform! You'll need to check and see if one of the existing ones will work and make sure proper defines are set, or write one. Please do a Github pull request if you do this for a new OS."
|
||||
#endif
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/* Linux NOTE: for default route override, some Linux distributions will
|
||||
* require a change to the rp_filter parameter. A value of '1' will prevent
|
||||
* default route override from working properly.
|
||||
*
|
||||
* sudo sysctl -w net.ipv4.conf.all.rp_filter=2
|
||||
*
|
||||
* Add to /etc/sysctl.conf or /etc/sysctl.d/... to make permanent.
|
||||
*
|
||||
* This is true of CentOS/RHEL 6+ and possibly others. This is because
|
||||
* Linux default route override implies asymmetric routes, which then
|
||||
* trigger Linux's "martian packet" filter. */
|
||||
|
||||
bool ManagedRoute::sync()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
NET_LUID interfaceLuid;
|
||||
interfaceLuid.Value = (ULONG64)Utils::hexStrToU64(_device); // on Windows we use the hex LUID as the "interface name" for ManagedRoute
|
||||
NET_IFINDEX interfaceIndex = -1;
|
||||
if (ConvertInterfaceLuidToIndex(&interfaceLuid,&interfaceIndex) != NO_ERROR)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
|
||||
/* In ZeroTier we create two more specific routes for every one route. We
|
||||
* do this for default routes and IPv4 routes other than /32s. If there
|
||||
* is a pre-existing system route that this route will override, we create
|
||||
* two more specific interface-bound shadow routes for it.
|
||||
*
|
||||
* This means that ZeroTier can *itself* continue communicating over
|
||||
* whatever physical routes might be present while simultaneously
|
||||
* overriding them for general system traffic. This is mostly for
|
||||
* "full tunnel" VPN modes of operation, but might be useful for
|
||||
* virtualizing physical networks in a hybrid design as well. */
|
||||
|
||||
// Generate two more specific routes than target with one extra bit
|
||||
InetAddress leftt,rightt;
|
||||
_forkTarget(_target,leftt,rightt);
|
||||
|
||||
#ifdef __BSD__ // ------------------------------------------------------------
|
||||
|
||||
// Find lowest metric system route that this route should override (if any)
|
||||
InetAddress newSystemVia;
|
||||
char newSystemDevice[128];
|
||||
newSystemDevice[0] = (char)0;
|
||||
int systemMetric = 9999999;
|
||||
std::vector<_RTE> rtes(_getRTEs(_target,false));
|
||||
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
||||
if (r->via) {
|
||||
if ((!newSystemVia)||(r->metric < systemMetric)) {
|
||||
newSystemVia = r->via;
|
||||
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
||||
systemMetric = r->metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((newSystemVia)&&(!newSystemDevice[0])) {
|
||||
rtes = _getRTEs(newSystemVia,true);
|
||||
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
||||
if (r->device[0]) {
|
||||
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow system route if it exists, also delete any obsolete shadows
|
||||
// and replace them with the new state. sync() is called periodically to
|
||||
// allow us to do that if underlying connectivity changes.
|
||||
if ( ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
|
||||
if ((_systemVia)&&(_systemDevice[0])) {
|
||||
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||
}
|
||||
|
||||
_systemVia = newSystemVia;
|
||||
Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
|
||||
|
||||
if ((_systemVia)&&(_systemDevice[0])) {
|
||||
_routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||
_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||
_routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||
_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply overriding non-device-scoped routes
|
||||
if (!_applied) {
|
||||
if (_via) {
|
||||
_routeCmd("add",leftt,_via,(const char *)0,(const char *)0);
|
||||
_routeCmd("change",leftt,_via,(const char *)0,(const char *)0);
|
||||
_routeCmd("add",rightt,_via,(const char *)0,(const char *)0);
|
||||
_routeCmd("change",rightt,_via,(const char *)0,(const char *)0);
|
||||
} else if (_device[0]) {
|
||||
_routeCmd("add",leftt,_via,(const char *)0,_device);
|
||||
_routeCmd("change",leftt,_via,(const char *)0,_device);
|
||||
_routeCmd("add",rightt,_via,(const char *)0,_device);
|
||||
_routeCmd("change",rightt,_via,(const char *)0,_device);
|
||||
}
|
||||
|
||||
_applied = true;
|
||||
}
|
||||
|
||||
#endif // __BSD__ ------------------------------------------------------------
|
||||
|
||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||
|
||||
if (!_applied) {
|
||||
_routeCmd("replace",leftt,_via,(_via) ? _device : (const char *)0);
|
||||
_routeCmd("replace",rightt,_via,(_via) ? _device : (const char *)0);
|
||||
_applied = true;
|
||||
}
|
||||
|
||||
#endif // __LINUX__ ----------------------------------------------------------
|
||||
|
||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||
|
||||
if (!_applied) {
|
||||
_winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
|
||||
_winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
|
||||
_applied = true;
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ --------------------------------------------------------
|
||||
|
||||
} else {
|
||||
|
||||
#ifdef __BSD__ // ------------------------------------------------------------
|
||||
|
||||
if (!_applied) {
|
||||
if (_via) {
|
||||
_routeCmd("add",_target,_via,(const char *)0,(const char *)0);
|
||||
_routeCmd("change",_target,_via,(const char *)0,(const char *)0);
|
||||
} else if (_device[0]) {
|
||||
_routeCmd("add",_target,_via,(const char *)0,_device);
|
||||
_routeCmd("change",_target,_via,(const char *)0,_device);
|
||||
}
|
||||
_applied = true;
|
||||
}
|
||||
|
||||
#endif // __BSD__ ------------------------------------------------------------
|
||||
|
||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||
|
||||
if (!_applied) {
|
||||
_routeCmd("replace",_target,_via,(_via) ? _device : (const char *)0);
|
||||
_applied = true;
|
||||
}
|
||||
|
||||
#endif // __LINUX__ ----------------------------------------------------------
|
||||
|
||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||
|
||||
if (!_applied) {
|
||||
_winRoute(false,interfaceLuid,interfaceIndex,_target,_via);
|
||||
_applied = true;
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ --------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ManagedRoute::remove()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
NET_LUID interfaceLuid;
|
||||
interfaceLuid.Value = (ULONG64)Utils::hexStrToU64(_device); // on Windows we use the hex LUID as the "interface name" for ManagedRoute
|
||||
NET_IFINDEX interfaceIndex = -1;
|
||||
if (ConvertInterfaceLuidToIndex(&interfaceLuid,&interfaceIndex) != NO_ERROR)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (_applied) {
|
||||
if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
|
||||
InetAddress leftt,rightt;
|
||||
_forkTarget(_target,leftt,rightt);
|
||||
|
||||
#ifdef __BSD__ // ------------------------------------------------------------
|
||||
|
||||
if ((_systemVia)&&(_systemDevice[0])) {
|
||||
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||
}
|
||||
if (_via) {
|
||||
_routeCmd("delete",leftt,_via,(const char *)0,(const char *)0);
|
||||
_routeCmd("delete",rightt,_via,(const char *)0,(const char *)0);
|
||||
} else if (_device[0]) {
|
||||
_routeCmd("delete",leftt,_via,(const char *)0,_device);
|
||||
_routeCmd("delete",rightt,_via,(const char *)0,_device);
|
||||
}
|
||||
|
||||
#endif // __BSD__ ------------------------------------------------------------
|
||||
|
||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||
|
||||
_routeCmd("del",leftt,_via,(_via) ? _device : (const char *)0);
|
||||
_routeCmd("del",rightt,_via,(_via) ? _device : (const char *)0);
|
||||
|
||||
#endif // __LINUX__ ----------------------------------------------------------
|
||||
|
||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||
|
||||
_winRoute(true,interfaceLuid,interfaceIndex,leftt,_via);
|
||||
_winRoute(true,interfaceLuid,interfaceIndex,rightt,_via);
|
||||
|
||||
#endif // __WINDOWS__ --------------------------------------------------------
|
||||
|
||||
} else {
|
||||
|
||||
#ifdef __BSD__ // ------------------------------------------------------------
|
||||
|
||||
if (_via) {
|
||||
_routeCmd("delete",_target,_via,(const char *)0,(const char *)0);
|
||||
} else if (_device[0]) {
|
||||
_routeCmd("delete",_target,_via,(const char *)0,_device);
|
||||
}
|
||||
|
||||
#endif // __BSD__ ------------------------------------------------------------
|
||||
|
||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||
|
||||
_routeCmd("del",_target,_via,(_via) ? _device : (const char *)0);
|
||||
|
||||
#endif // __LINUX__ ----------------------------------------------------------
|
||||
|
||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||
|
||||
_winRoute(true,interfaceLuid,interfaceIndex,_target,_via);
|
||||
|
||||
#endif // __WINDOWS__ --------------------------------------------------------
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
_target.zero();
|
||||
_via.zero();
|
||||
_systemVia.zero();
|
||||
_device[0] = (char)0;
|
||||
_systemDevice[0] = (char)0;
|
||||
_applied = false;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
107
osdep/ManagedRoute.hpp
Normal file
107
osdep/ManagedRoute.hpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
#ifndef ZT_MANAGEDROUTE_HPP
|
||||
#define ZT_MANAGEDROUTE_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
|
||||
*/
|
||||
class ManagedRoute
|
||||
{
|
||||
public:
|
||||
ManagedRoute()
|
||||
{
|
||||
_device[0] = (char)0;
|
||||
_systemDevice[0] = (char)0;
|
||||
_applied = false;
|
||||
}
|
||||
|
||||
~ManagedRoute()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
ManagedRoute(const ManagedRoute &r)
|
||||
{
|
||||
_applied = false;
|
||||
*this = r;
|
||||
}
|
||||
|
||||
inline ManagedRoute &operator=(const ManagedRoute &r)
|
||||
{
|
||||
if ((!_applied)&&(!r._applied)) {
|
||||
memcpy(this,&r,sizeof(ManagedRoute)); // InetAddress is memcpy'able
|
||||
} else {
|
||||
fprintf(stderr,"Applied ManagedRoute isn't copyable!\n");
|
||||
abort();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize object and set route
|
||||
*
|
||||
* Note: on Windows, use the interface NET_LUID in hexadecimal as the
|
||||
* "device name."
|
||||
*
|
||||
* @param target Route target (e.g. 0.0.0.0/0 for default)
|
||||
* @param via Route next L3 hop or NULL InetAddress if local in which case it will be routed via device
|
||||
* @param device Name or hex LUID of ZeroTier device (e.g. zt#)
|
||||
* @return True if route was successfully set
|
||||
*/
|
||||
inline bool set(const InetAddress &target,const InetAddress &via,const char *device)
|
||||
{
|
||||
if ((!via)&&(!device[0]))
|
||||
return false;
|
||||
this->remove();
|
||||
_target = target;
|
||||
_via = via;
|
||||
Utils::scopy(_device,sizeof(_device),device);
|
||||
return this->sync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or update currently set route
|
||||
*
|
||||
* This must be called periodically for routes that shadow others so that
|
||||
* shadow routes can be updated. In some cases it has no effect
|
||||
*
|
||||
* @return True if route add/update was successful
|
||||
*/
|
||||
bool sync();
|
||||
|
||||
/**
|
||||
* Remove and clear this ManagedRoute
|
||||
*
|
||||
* This does nothing if this ManagedRoute is not set or has already been
|
||||
* removed. If this is not explicitly called it is called automatically on
|
||||
* destruct.
|
||||
*/
|
||||
void remove();
|
||||
|
||||
inline const InetAddress &target() const { return _target; }
|
||||
inline const InetAddress &via() const { return _via; }
|
||||
inline const char *device() const { return _device; }
|
||||
|
||||
private:
|
||||
|
||||
InetAddress _target;
|
||||
InetAddress _via;
|
||||
InetAddress _systemVia; // for route overrides
|
||||
char _device[128];
|
||||
char _systemDevice[128]; // for route overrides
|
||||
bool _applied;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
273
osdep/OSUtils.cpp
Normal file
273
osdep/OSUtils.cpp
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <dirent.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#include <ShlObj.h>
|
||||
#include <netioapi.h>
|
||||
#include <iphlpapi.h>
|
||||
#endif
|
||||
|
||||
#include "OSUtils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
|
||||
throw()
|
||||
{
|
||||
int fdout = ::open(stdoutPath,O_WRONLY|O_CREAT,0600);
|
||||
if (fdout > 0) {
|
||||
int fderr;
|
||||
if (stderrPath) {
|
||||
fderr = ::open(stderrPath,O_WRONLY|O_CREAT,0600);
|
||||
if (fderr <= 0) {
|
||||
::close(fdout);
|
||||
return false;
|
||||
}
|
||||
} else fderr = fdout;
|
||||
::close(STDOUT_FILENO);
|
||||
::close(STDERR_FILENO);
|
||||
::dup2(fdout,STDOUT_FILENO);
|
||||
::dup2(fderr,STDERR_FILENO);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // __UNIX_LIKE__
|
||||
|
||||
std::vector<std::string> OSUtils::listDirectory(const char *path)
|
||||
{
|
||||
std::vector<std::string> r;
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
HANDLE hFind;
|
||||
WIN32_FIND_DATAA ffd;
|
||||
if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if ((strcmp(ffd.cFileName,"."))&&(strcmp(ffd.cFileName,".."))&&((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0))
|
||||
r.push_back(std::string(ffd.cFileName));
|
||||
} while (FindNextFileA(hFind,&ffd));
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
struct dirent de;
|
||||
struct dirent *dptr;
|
||||
DIR *d = opendir(path);
|
||||
if (!d)
|
||||
return r;
|
||||
dptr = (struct dirent *)0;
|
||||
for(;;) {
|
||||
if (readdir_r(d,&de,&dptr))
|
||||
break;
|
||||
if (dptr) {
|
||||
if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&(dptr->d_type != DT_DIR))
|
||||
r.push_back(std::string(dptr->d_name));
|
||||
} else break;
|
||||
}
|
||||
closedir(d);
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void OSUtils::lockDownFile(const char *path,bool isDir)
|
||||
{
|
||||
#ifdef __UNIX_LIKE__
|
||||
chmod(path,isDir ? 0700 : 0600);
|
||||
#else
|
||||
#ifdef __WINDOWS__
|
||||
{
|
||||
STARTUPINFOA startupInfo;
|
||||
PROCESS_INFORMATION processInfo;
|
||||
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
memset(&startupInfo,0,sizeof(STARTUPINFOA));
|
||||
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
|
||||
if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /inheritance:d /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
|
||||
WaitForSingleObject(processInfo.hProcess,INFINITE);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
memset(&startupInfo,0,sizeof(STARTUPINFOA));
|
||||
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
|
||||
if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove *S-1-5-32-545 /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
|
||||
WaitForSingleObject(processInfo.hProcess,INFINITE);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t OSUtils::getLastModified(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
if (stat(path,&s))
|
||||
return 0;
|
||||
return (((uint64_t)s.st_mtime) * 1000ULL);
|
||||
}
|
||||
|
||||
bool OSUtils::fileExists(const char *path,bool followLinks)
|
||||
{
|
||||
struct stat s;
|
||||
#ifdef __UNIX_LIKE__
|
||||
if (!followLinks)
|
||||
return (lstat(path,&s) == 0);
|
||||
#endif
|
||||
return (stat(path,&s) == 0);
|
||||
}
|
||||
|
||||
int64_t OSUtils::getFileSize(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
if (stat(path,&s))
|
||||
return -1;
|
||||
#ifdef __WINDOWS__
|
||||
return s.st_size;
|
||||
#else
|
||||
if (S_ISREG(s.st_mode))
|
||||
return s.st_size;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> OSUtils::resolve(const char *name)
|
||||
{
|
||||
std::vector<InetAddress> r;
|
||||
std::vector<InetAddress>::iterator i;
|
||||
InetAddress tmp;
|
||||
struct addrinfo *ai = (struct addrinfo *)0,*p;
|
||||
if (!getaddrinfo(name,(const char *)0,(const struct addrinfo *)0,&ai)) {
|
||||
try {
|
||||
p = ai;
|
||||
while (p) {
|
||||
if ((p->ai_addr)&&((p->ai_addr->sa_family == AF_INET)||(p->ai_addr->sa_family == AF_INET6))) {
|
||||
tmp = *(p->ai_addr);
|
||||
for(i=r.begin();i!=r.end();++i) {
|
||||
if (i->ipsEqual(tmp))
|
||||
goto skip_add_inetaddr;
|
||||
}
|
||||
r.push_back(tmp);
|
||||
}
|
||||
skip_add_inetaddr:
|
||||
p = p->ai_next;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
std::sort(r.begin(),r.end());
|
||||
return r;
|
||||
}
|
||||
|
||||
bool OSUtils::readFile(const char *path,std::string &buf)
|
||||
{
|
||||
char tmp[1024];
|
||||
FILE *f = fopen(path,"rb");
|
||||
if (f) {
|
||||
for(;;) {
|
||||
long n = (long)fread(tmp,1,sizeof(tmp),f);
|
||||
if (n > 0)
|
||||
buf.append(tmp,n);
|
||||
else break;
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
|
||||
{
|
||||
FILE *f = fopen(path,"wb");
|
||||
if (f) {
|
||||
if ((long)fwrite(buf,1,len,f) != (long)len) {
|
||||
fclose(f);
|
||||
return false;
|
||||
} else {
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string OSUtils::platformDefaultHomePath()
|
||||
{
|
||||
#ifdef __UNIX_LIKE__
|
||||
|
||||
#ifdef __APPLE__
|
||||
// /Library/... on Apple
|
||||
return std::string("/Library/Application Support/ZeroTier/One");
|
||||
#else
|
||||
|
||||
#ifdef __BSD__
|
||||
// BSD likes /var/db instead of /var/lib
|
||||
return std::string("/var/db/zerotier-one");
|
||||
#else
|
||||
// Use /var/lib for Linux and other *nix
|
||||
return std::string("/var/lib/zerotier-one");
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#else // not __UNIX_LIKE__
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
// Look up app data folder on Windows, e.g. C:\ProgramData\...
|
||||
char buf[16384];
|
||||
if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf)))
|
||||
return (std::string(buf) + "\\ZeroTier\\One");
|
||||
else return std::string("C:\\ZeroTier\\One");
|
||||
#else
|
||||
|
||||
return (std::string(ZT_PATH_SEPARATOR_S) + "ZeroTier" + ZT_PATH_SEPARATOR_S + "One"); // UNKNOWN PLATFORM
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __UNIX_LIKE__ or not...
|
||||
}
|
||||
|
||||
// Used to convert HTTP header names to ASCII lower case
|
||||
const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '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', '{', '|', '}', '~', '_', '`', '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', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
|
||||
|
||||
} // namespace ZeroTier
|
249
osdep/OSUtils.hpp
Normal file
249
osdep/OSUtils.hpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_OSUTILS_HPP
|
||||
#define ZT_OSUTILS_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <Shlwapi.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Miscellaneous utility functions and global constants
|
||||
*/
|
||||
class OSUtils
|
||||
{
|
||||
public:
|
||||
#ifdef __UNIX_LIKE__
|
||||
/**
|
||||
* Close STDOUT_FILENO and STDERR_FILENO and replace them with output to given path
|
||||
*
|
||||
* This can be called after fork() and prior to exec() to suppress output
|
||||
* from a subprocess, such as auto-update.
|
||||
*
|
||||
* @param stdoutPath Path to file to use for stdout
|
||||
* @param stderrPath Path to file to use for stderr, or NULL for same as stdout (default)
|
||||
* @return True on success
|
||||
*/
|
||||
static bool redirectUnixOutputs(const char *stdoutPath,const char *stderrPath = (const char *)0)
|
||||
throw();
|
||||
#endif // __UNIX_LIKE__
|
||||
|
||||
/**
|
||||
* Delete a file
|
||||
*
|
||||
* @param path Path to delete
|
||||
* @return True if delete was successful
|
||||
*/
|
||||
static inline bool rm(const char *path)
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (DeleteFileA(path) != FALSE);
|
||||
#else
|
||||
return (unlink(path) == 0);
|
||||
#endif
|
||||
}
|
||||
static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); }
|
||||
|
||||
static inline bool mkdir(const char *path)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
if (::PathIsDirectoryA(path))
|
||||
return true;
|
||||
return (::CreateDirectoryA(path,NULL) == TRUE);
|
||||
#else
|
||||
if (::mkdir(path,0755) != 0)
|
||||
return (errno == EEXIST);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
static inline bool mkdir(const std::string &path) throw() { return OSUtils::mkdir(path.c_str()); }
|
||||
|
||||
/**
|
||||
* List a directory's contents
|
||||
*
|
||||
* This returns only files, not sub-directories.
|
||||
*
|
||||
* @param path Path to list
|
||||
* @return Names of files in directory
|
||||
*/
|
||||
static std::vector<std::string> listDirectory(const char *path);
|
||||
|
||||
/**
|
||||
* Set modes on a file to something secure
|
||||
*
|
||||
* This locks a file so that only the owner can access it. What it actually
|
||||
* does varies by platform.
|
||||
*
|
||||
* @param path Path to lock
|
||||
* @param isDir True if this is a directory
|
||||
*/
|
||||
static void lockDownFile(const char *path,bool isDir);
|
||||
|
||||
/**
|
||||
* Get file last modification time
|
||||
*
|
||||
* Resolution is often only second, not millisecond, but the return is
|
||||
* always in ms for comparison against now().
|
||||
*
|
||||
* @param path Path to file to get time
|
||||
* @return Last modification time in ms since epoch or 0 if not found
|
||||
*/
|
||||
static uint64_t getLastModified(const char *path);
|
||||
|
||||
/**
|
||||
* @param path Path to check
|
||||
* @param followLinks Follow links (on platforms with that concept)
|
||||
* @return True if file or directory exists at path location
|
||||
*/
|
||||
static bool fileExists(const char *path,bool followLinks = true);
|
||||
|
||||
/**
|
||||
* @param path Path to file
|
||||
* @return File size or -1 if nonexistent or other failure
|
||||
*/
|
||||
static int64_t getFileSize(const char *path);
|
||||
|
||||
/**
|
||||
* Get IP (v4 and/or v6) addresses for a given host
|
||||
*
|
||||
* This is a blocking resolver.
|
||||
*
|
||||
* @param name Host name
|
||||
* @return IP addresses in InetAddress sort order or empty vector if not found
|
||||
*/
|
||||
static std::vector<InetAddress> resolve(const char *name);
|
||||
|
||||
/**
|
||||
* @return Current time in milliseconds since epoch
|
||||
*/
|
||||
static inline uint64_t now()
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
FILETIME ft;
|
||||
SYSTEMTIME st;
|
||||
ULARGE_INTEGER tmp;
|
||||
GetSystemTime(&st);
|
||||
SystemTimeToFileTime(&st,&ft);
|
||||
tmp.LowPart = ft.dwLowDateTime;
|
||||
tmp.HighPart = ft.dwHighDateTime;
|
||||
return ( ((tmp.QuadPart - 116444736000000000ULL) / 10000L) + st.wMilliseconds );
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,(struct timezone *)0);
|
||||
return ( (1000ULL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) );
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @return Current time in seconds since epoch, to the highest available resolution
|
||||
*/
|
||||
static inline double nowf()
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
FILETIME ft;
|
||||
SYSTEMTIME st;
|
||||
ULARGE_INTEGER tmp;
|
||||
GetSystemTime(&st);
|
||||
SystemTimeToFileTime(&st,&ft);
|
||||
tmp.LowPart = ft.dwLowDateTime;
|
||||
tmp.HighPart = ft.dwHighDateTime;
|
||||
return (((double)(tmp.QuadPart - 116444736000000000ULL)) / 10000000.0);
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,(struct timezone *)0);
|
||||
return ( ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0) );
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the full contents of a file into a string buffer
|
||||
*
|
||||
* The buffer isn't cleared, so if it already contains data the file's data will
|
||||
* be appended.
|
||||
*
|
||||
* @param path Path of file to read
|
||||
* @param buf Buffer to fill
|
||||
* @return True if open and read successful
|
||||
*/
|
||||
static bool readFile(const char *path,std::string &buf);
|
||||
|
||||
/**
|
||||
* Write a block of data to disk, replacing any current file contents
|
||||
*
|
||||
* @param path Path to write
|
||||
* @param buf Buffer containing data
|
||||
* @param len Length of buffer
|
||||
* @return True if entire file was successfully written
|
||||
*/
|
||||
static bool writeFile(const char *path,const void *buf,unsigned int len);
|
||||
|
||||
/**
|
||||
* Write a block of data to disk, replacing any current file contents
|
||||
*
|
||||
* @param path Path to write
|
||||
* @param s Data to write
|
||||
* @return True if entire file was successfully written
|
||||
*/
|
||||
static inline bool writeFile(const char *path,const std::string &s) { return writeFile(path,s.data(),(unsigned int)s.length()); }
|
||||
|
||||
/**
|
||||
* @param c ASCII character to convert
|
||||
* @return Lower case ASCII character or unchanged if not a letter
|
||||
*/
|
||||
static inline char toLower(char c) throw() { return (char)OSUtils::TOLOWER_TABLE[(unsigned long)c]; }
|
||||
|
||||
/**
|
||||
* @return Platform default ZeroTier One home path
|
||||
*/
|
||||
static std::string platformDefaultHomePath();
|
||||
|
||||
private:
|
||||
static const unsigned char TOLOWER_TABLE[256];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
659
osdep/OSXEthernetTap.cpp
Normal file
659
osdep/OSXEthernetTap.cpp
Normal file
|
@ -0,0 +1,659 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#include <netinet/in_var.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
// OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!?
|
||||
struct prf_ra {
|
||||
u_char onlink : 1;
|
||||
u_char autonomous : 1;
|
||||
u_char reserved : 6;
|
||||
} prf_ra;
|
||||
|
||||
#include <netinet6/nd6.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
// These are KERNEL_PRIVATE... why?
|
||||
#ifndef SIOCAUTOCONF_START
|
||||
#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */
|
||||
#endif
|
||||
#ifndef SIOCAUTOCONF_STOP
|
||||
#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------
|
||||
// This source is from:
|
||||
// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt
|
||||
// It's here because OSX 10.6 does not have this convenience function.
|
||||
|
||||
#define SALIGN (sizeof(uint32_t) - 1)
|
||||
#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
|
||||
(SALIGN + 1))
|
||||
#define MAX_SYSCTL_TRY 5
|
||||
#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
|
||||
|
||||
/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */
|
||||
/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */
|
||||
//#define DARWIN_COMPAT
|
||||
|
||||
//#ifdef DARWIN_COMPAT
|
||||
#define GIM_SYSCTL_MIB NET_RT_IFLIST2
|
||||
#define GIM_RTM_ADDR RTM_NEWMADDR2
|
||||
//#else
|
||||
//#define GIM_SYSCTL_MIB NET_RT_IFMALIST
|
||||
//#define GIM_RTM_ADDR RTM_NEWMADDR
|
||||
//#endif
|
||||
|
||||
// Not in 10.6 includes so use our own
|
||||
struct _intl_ifmaddrs {
|
||||
struct _intl_ifmaddrs *ifma_next;
|
||||
struct sockaddr *ifma_name;
|
||||
struct sockaddr *ifma_addr;
|
||||
struct sockaddr *ifma_lladdr;
|
||||
};
|
||||
|
||||
static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif)
|
||||
{
|
||||
int icnt = 1;
|
||||
int dcnt = 0;
|
||||
int ntry = 0;
|
||||
size_t len;
|
||||
size_t needed;
|
||||
int mib[6];
|
||||
int i;
|
||||
char *buf;
|
||||
char *data;
|
||||
char *next;
|
||||
char *p;
|
||||
struct ifma_msghdr2 *ifmam;
|
||||
struct _intl_ifmaddrs *ifa, *ift;
|
||||
struct rt_msghdr *rtm;
|
||||
struct sockaddr *sa;
|
||||
|
||||
mib[0] = CTL_NET;
|
||||
mib[1] = PF_ROUTE;
|
||||
mib[2] = 0; /* protocol */
|
||||
mib[3] = 0; /* wildcard address family */
|
||||
mib[4] = GIM_SYSCTL_MIB;
|
||||
mib[5] = 0; /* no flags */
|
||||
do {
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
|
||||
return (-1);
|
||||
if ((buf = (char *)malloc(needed)) == NULL)
|
||||
return (-1);
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
|
||||
if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
|
||||
free(buf);
|
||||
return (-1);
|
||||
}
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
} while (buf == NULL);
|
||||
|
||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
||||
rtm = (struct rt_msghdr *)(void *)next;
|
||||
if (rtm->rtm_version != RTM_VERSION)
|
||||
continue;
|
||||
switch (rtm->rtm_type) {
|
||||
case GIM_RTM_ADDR:
|
||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
||||
break;
|
||||
icnt++;
|
||||
p = (char *)(ifmam + 1);
|
||||
for (i = 0; i < RTAX_MAX; i++) {
|
||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
||||
(1 << i)) == 0)
|
||||
continue;
|
||||
sa = (struct sockaddr *)(void *)p;
|
||||
len = SA_RLEN(sa);
|
||||
dcnt += len;
|
||||
p += len;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt);
|
||||
if (data == NULL) {
|
||||
free(buf);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ifa = (struct _intl_ifmaddrs *)(void *)data;
|
||||
data += sizeof(struct _intl_ifmaddrs) * icnt;
|
||||
|
||||
memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt);
|
||||
ift = ifa;
|
||||
|
||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
||||
rtm = (struct rt_msghdr *)(void *)next;
|
||||
if (rtm->rtm_version != RTM_VERSION)
|
||||
continue;
|
||||
|
||||
switch (rtm->rtm_type) {
|
||||
case GIM_RTM_ADDR:
|
||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
||||
break;
|
||||
|
||||
p = (char *)(ifmam + 1);
|
||||
for (i = 0; i < RTAX_MAX; i++) {
|
||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
||||
(1 << i)) == 0)
|
||||
continue;
|
||||
sa = (struct sockaddr *)(void *)p;
|
||||
len = SA_RLEN(sa);
|
||||
switch (i) {
|
||||
case RTAX_GATEWAY:
|
||||
ift->ifma_lladdr =
|
||||
(struct sockaddr *)(void *)data;
|
||||
memcpy(data, p, len);
|
||||
data += len;
|
||||
break;
|
||||
|
||||
case RTAX_IFP:
|
||||
ift->ifma_name =
|
||||
(struct sockaddr *)(void *)data;
|
||||
memcpy(data, p, len);
|
||||
data += len;
|
||||
break;
|
||||
|
||||
case RTAX_IFA:
|
||||
ift->ifma_addr =
|
||||
(struct sockaddr *)(void *)data;
|
||||
memcpy(data, p, len);
|
||||
data += len;
|
||||
break;
|
||||
|
||||
default:
|
||||
data += len;
|
||||
break;
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
ift->ifma_next = ift + 1;
|
||||
ift = ift->ifma_next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
if (ift > ifa) {
|
||||
ift--;
|
||||
ift->ifma_next = NULL;
|
||||
*pif = ifa;
|
||||
} else {
|
||||
*pif = NULL;
|
||||
free(ifa);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp)
|
||||
{
|
||||
free(ifmp);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "../node/Dictionary.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
#include "OSXEthernetTap.hpp"
|
||||
|
||||
// ff:ff:ff:ff:ff:ff with no ADI
|
||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
||||
|
||||
static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts)
|
||||
{
|
||||
struct in6_ndireq nd;
|
||||
struct in6_ifreq ifr;
|
||||
|
||||
int s = socket(AF_INET6,SOCK_DGRAM,0);
|
||||
if (s <= 0)
|
||||
return false;
|
||||
|
||||
memset(&nd,0,sizeof(nd));
|
||||
strncpy(nd.ifname,ifname,sizeof(nd.ifname));
|
||||
|
||||
if (ioctl(s,SIOCGIFINFO_IN6,&nd)) {
|
||||
close(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long oldFlags = (unsigned long)nd.ndi.flags;
|
||||
|
||||
if (performNUD)
|
||||
nd.ndi.flags |= ND6_IFF_PERFORMNUD;
|
||||
else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD;
|
||||
|
||||
if (oldFlags != (unsigned long)nd.ndi.flags) {
|
||||
if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) {
|
||||
close(s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&ifr,0,sizeof(ifr));
|
||||
strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name));
|
||||
if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) {
|
||||
close(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
close(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static long globalTapsRunning = 0;
|
||||
static Mutex globalTapCreateLock;
|
||||
|
||||
OSXEthernetTap::OSXEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
|
||||
void *arg) :
|
||||
_handler(handler),
|
||||
_arg(arg),
|
||||
_nwid(nwid),
|
||||
_homePath(homePath),
|
||||
_mtu(mtu),
|
||||
_metric(metric),
|
||||
_fd(0),
|
||||
_enabled(true)
|
||||
{
|
||||
char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
|
||||
struct stat stattmp;
|
||||
|
||||
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
|
||||
|
||||
if (mtu > 2800)
|
||||
throw std::runtime_error("max tap MTU is 2800");
|
||||
|
||||
Mutex::Lock _gl(globalTapCreateLock);
|
||||
|
||||
if (::stat("/dev/zt0",&stattmp)) {
|
||||
long kextpid = (long)vfork();
|
||||
if (kextpid == 0) {
|
||||
::chdir(homePath);
|
||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
||||
::execl("/sbin/kextload","/sbin/kextload","-q","-repository",homePath,"tap.kext",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (kextpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(kextpid,&exitcode,0);
|
||||
}
|
||||
::usleep(500); // give tap device driver time to start up and try again
|
||||
if (::stat("/dev/zt0",&stattmp))
|
||||
throw std::runtime_error("/dev/zt# tap devices do not exist and cannot load tap.kext");
|
||||
}
|
||||
|
||||
// Try to reopen the last device we had, if we had one and it's still unused.
|
||||
bool recalledDevice = false;
|
||||
std::string devmapbuf;
|
||||
Dictionary<8194> devmap;
|
||||
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
|
||||
devmap.load(devmapbuf.c_str());
|
||||
char desiredDevice[128];
|
||||
if (devmap.get(nwids,desiredDevice,sizeof(desiredDevice)) > 0) {
|
||||
Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice);
|
||||
if (stat(devpath,&stattmp) == 0) {
|
||||
_fd = ::open(devpath,O_RDWR);
|
||||
if (_fd > 0) {
|
||||
_dev = desiredDevice;
|
||||
recalledDevice = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the first unused tap device if we didn't recall a previous one.
|
||||
if (!recalledDevice) {
|
||||
for(int i=0;i<64;++i) {
|
||||
Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
|
||||
if (stat(devpath,&stattmp))
|
||||
throw std::runtime_error("no more TAP devices available");
|
||||
_fd = ::open(devpath,O_RDWR);
|
||||
if (_fd > 0) {
|
||||
char foo[16];
|
||||
Utils::snprintf(foo,sizeof(foo),"zt%d",i);
|
||||
_dev = foo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_fd <= 0)
|
||||
throw std::runtime_error("unable to open TAP device or no more devices available");
|
||||
|
||||
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
|
||||
::close(_fd);
|
||||
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
|
||||
}
|
||||
|
||||
// Configure MAC address and MTU, bring interface up
|
||||
Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
|
||||
Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
|
||||
Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
if (exitcode) {
|
||||
::close(_fd);
|
||||
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
|
||||
}
|
||||
}
|
||||
|
||||
_setIpv6Stuff(_dev.c_str(),true,false);
|
||||
|
||||
// Set close-on-exec so that devices cannot persist if we fork/exec for update
|
||||
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
::pipe(_shutdownSignalPipe);
|
||||
|
||||
++globalTapsRunning;
|
||||
|
||||
devmap.erase(nwids);
|
||||
devmap.add(nwids,_dev.c_str());
|
||||
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),(const void *)devmap.data(),devmap.sizeBytes());
|
||||
|
||||
_thread = Thread::start(this);
|
||||
}
|
||||
|
||||
OSXEthernetTap::~OSXEthernetTap()
|
||||
{
|
||||
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
||||
Thread::join(_thread);
|
||||
|
||||
::close(_fd);
|
||||
::close(_shutdownSignalPipe[0]);
|
||||
::close(_shutdownSignalPipe[1]);
|
||||
|
||||
{
|
||||
Mutex::Lock _gl(globalTapCreateLock);
|
||||
if (--globalTapsRunning <= 0) {
|
||||
globalTapsRunning = 0; // sanity check -- should not be possible
|
||||
|
||||
char tmp[16384];
|
||||
sprintf(tmp,"%s/%s",_homePath.c_str(),"tap.kext");
|
||||
long kextpid = (long)vfork();
|
||||
if (kextpid == 0) {
|
||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
||||
::execl("/sbin/kextunload","/sbin/kextunload",tmp,(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (kextpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(kextpid,&exitcode,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSXEthernetTap::setEnabled(bool en)
|
||||
{
|
||||
_enabled = en;
|
||||
// TODO: interface status change
|
||||
}
|
||||
|
||||
bool OSXEthernetTap::enabled() const
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
bool OSXEthernetTap::addIp(const InetAddress &ip)
|
||||
{
|
||||
if (!ip)
|
||||
return false;
|
||||
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0);
|
||||
::_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
::waitpid(cpid,&exitcode,0);
|
||||
return (exitcode == 0);
|
||||
} // else return false...
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OSXEthernetTap::removeIp(const InetAddress &ip)
|
||||
{
|
||||
if (!ip)
|
||||
return true;
|
||||
std::vector<InetAddress> allIps(ips());
|
||||
for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
|
||||
if (*i == ip) {
|
||||
long cpid = (long)vfork();
|
||||
if (cpid == 0) {
|
||||
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0);
|
||||
_exit(-1);
|
||||
} else if (cpid > 0) {
|
||||
int exitcode = -1;
|
||||
waitpid(cpid,&exitcode,0);
|
||||
return (exitcode == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> OSXEthernetTap::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 OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
||||
{
|
||||
char putBuf[4096];
|
||||
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
|
||||
to.copyTo(putBuf,6);
|
||||
from.copyTo(putBuf + 6,6);
|
||||
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
|
||||
memcpy(putBuf + 14,data,len);
|
||||
len += 14;
|
||||
::write(_fd,putBuf,len);
|
||||
}
|
||||
}
|
||||
|
||||
std::string OSXEthernetTap::deviceName() const
|
||||
{
|
||||
return _dev;
|
||||
}
|
||||
|
||||
void OSXEthernetTap::setFriendlyName(const char *friendlyName)
|
||||
{
|
||||
}
|
||||
|
||||
void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
|
||||
{
|
||||
std::vector<MulticastGroup> newGroups;
|
||||
|
||||
struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0;
|
||||
if (!_intl_getifmaddrs(&ifmap)) {
|
||||
struct _intl_ifmaddrs *p = ifmap;
|
||||
while (p) {
|
||||
if (p->ifma_addr->sa_family == AF_LINK) {
|
||||
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
|
||||
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
|
||||
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
|
||||
newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
|
||||
}
|
||||
p = p->ifma_next;
|
||||
}
|
||||
_intl_freeifmaddrs(ifmap);
|
||||
}
|
||||
|
||||
std::vector<InetAddress> allIps(ips());
|
||||
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
||||
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
|
||||
|
||||
std::sort(newGroups.begin(),newGroups.end());
|
||||
std::unique(newGroups.begin(),newGroups.end());
|
||||
|
||||
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
|
||||
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
|
||||
added.push_back(*m);
|
||||
}
|
||||
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
|
||||
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
|
||||
removed.push_back(*m);
|
||||
}
|
||||
|
||||
_multicastGroups.swap(newGroups);
|
||||
}
|
||||
|
||||
void OSXEthernetTap::threadMain()
|
||||
throw()
|
||||
{
|
||||
fd_set readfds,nullfds;
|
||||
MAC to,from;
|
||||
int n,nfds,r;
|
||||
char getBuf[8194];
|
||||
|
||||
Thread::sleep(500);
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&nullfds);
|
||||
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
|
||||
|
||||
r = 0;
|
||||
for(;;) {
|
||||
FD_SET(_shutdownSignalPipe[0],&readfds);
|
||||
FD_SET(_fd,&readfds);
|
||||
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
|
||||
|
||||
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
|
||||
break;
|
||||
|
||||
if (FD_ISSET(_fd,&readfds)) {
|
||||
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
|
||||
if (n < 0) {
|
||||
if ((errno != EINTR)&&(errno != ETIMEDOUT))
|
||||
break;
|
||||
} else {
|
||||
// Some tap drivers like to send the ethernet frame and the
|
||||
// payload in two chunks, so handle that by accumulating
|
||||
// data until we have at least a frame.
|
||||
r += n;
|
||||
if (r > 14) {
|
||||
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
|
||||
r = _mtu + 14;
|
||||
|
||||
if (_enabled) {
|
||||
to.setTo(getBuf,6);
|
||||
from.setTo(getBuf + 6,6);
|
||||
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
|
||||
// TODO: VLAN support
|
||||
_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
86
osdep/OSXEthernetTap.hpp
Normal file
86
osdep/OSXEthernetTap.hpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_OSXETHERNETTAP_HPP
|
||||
#define ZT_OSXETHERNETTAP_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/MAC.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../node/MulticastGroup.hpp"
|
||||
|
||||
#include "Thread.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* OSX Ethernet tap using ZeroTier kernel extension zt# devices
|
||||
*/
|
||||
class OSXEthernetTap
|
||||
{
|
||||
public:
|
||||
OSXEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg);
|
||||
|
||||
~OSXEthernetTap();
|
||||
|
||||
void setEnabled(bool en);
|
||||
bool enabled() const;
|
||||
bool addIp(const InetAddress &ip);
|
||||
bool removeIp(const InetAddress &ip);
|
||||
std::vector<InetAddress> ips() const;
|
||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||
std::string deviceName() const;
|
||||
void setFriendlyName(const char *friendlyName);
|
||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
||||
|
||||
void threadMain()
|
||||
throw();
|
||||
|
||||
private:
|
||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
||||
void *_arg;
|
||||
uint64_t _nwid;
|
||||
Thread _thread;
|
||||
std::string _homePath;
|
||||
std::string _dev;
|
||||
std::vector<MulticastGroup> _multicastGroups;
|
||||
unsigned int _mtu;
|
||||
unsigned int _metric;
|
||||
int _fd;
|
||||
int _shutdownSignalPipe[2];
|
||||
volatile bool _enabled;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
1115
osdep/Phy.hpp
Normal file
1115
osdep/Phy.hpp
Normal file
File diff suppressed because it is too large
Load diff
325
osdep/PortMapper.cpp
Normal file
325
osdep/PortMapper.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef ZT_USE_MINIUPNPC
|
||||
|
||||
// Uncomment to dump debug messages
|
||||
//#define ZT_PORTMAPPER_TRACE 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../node/Utils.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
#include "PortMapper.hpp"
|
||||
|
||||
// These must be defined to get rid of dynamic export stuff in libminiupnpc and libnatpmp
|
||||
#ifdef __WINDOWS__
|
||||
#ifndef MINIUPNP_STATICLIB
|
||||
#define MINIUPNP_STATICLIB
|
||||
#endif
|
||||
#ifndef STATICLIB
|
||||
#define STATICLIB
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ZT_USE_SYSTEM_MINIUPNPC
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
#else
|
||||
#include "../ext/miniupnpc/miniupnpc.h"
|
||||
#include "../ext/miniupnpc/upnpcommands.h"
|
||||
#endif
|
||||
|
||||
#ifdef ZT_USE_SYSTEM_NATPMP
|
||||
#include <natpmp.h>
|
||||
#else
|
||||
#include "../ext/libnatpmp/natpmp.h"
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class PortMapperImpl
|
||||
{
|
||||
public:
|
||||
PortMapperImpl(int localUdpPortToMap,const char *un) :
|
||||
run(true),
|
||||
localPort(localUdpPortToMap),
|
||||
uniqueName(un)
|
||||
{
|
||||
}
|
||||
|
||||
~PortMapperImpl() {}
|
||||
|
||||
void threadMain()
|
||||
throw()
|
||||
{
|
||||
int mode = 0; // 0 == NAT-PMP, 1 == UPnP
|
||||
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: started for UDP port %d"ZT_EOL_S,localPort);
|
||||
#endif
|
||||
|
||||
while (run) {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// NAT-PMP mode (preferred)
|
||||
// ---------------------------------------------------------------------
|
||||
if (mode == 0) {
|
||||
natpmp_t natpmp;
|
||||
natpmpresp_t response;
|
||||
int r = 0;
|
||||
|
||||
bool natPmpSuccess = false;
|
||||
for(int tries=0;tries<60;++tries) {
|
||||
int tryPort = (int)localPort + tries;
|
||||
if (tryPort >= 65535)
|
||||
tryPort = (tryPort - 65535) + 1025;
|
||||
|
||||
memset(&natpmp,0,sizeof(natpmp));
|
||||
memset(&response,0,sizeof(response));
|
||||
|
||||
if (initnatpmp(&natpmp,0,0) != 0) {
|
||||
mode = 1;
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: NAT-PMP: init failed, switching to UPnP mode"ZT_EOL_S);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
InetAddress publicAddress;
|
||||
sendpublicaddressrequest(&natpmp);
|
||||
uint64_t myTimeout = OSUtils::now() + 5000;
|
||||
do {
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(natpmp.s, &fds);
|
||||
getnatpmprequesttimeout(&natpmp, &timeout);
|
||||
select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
|
||||
r = readnatpmpresponseorretry(&natpmp, &response);
|
||||
if (OSUtils::now() >= myTimeout)
|
||||
break;
|
||||
} while (r == NATPMP_TRYAGAIN);
|
||||
if (r == 0) {
|
||||
publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0);
|
||||
} else {
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: NAT-PMP: request for external address failed, aborting..."ZT_EOL_S);
|
||||
#endif
|
||||
closenatpmp(&natpmp);
|
||||
break;
|
||||
}
|
||||
|
||||
sendnewportmappingrequest(&natpmp,NATPMP_PROTOCOL_UDP,localPort,tryPort,(ZT_PORTMAPPER_REFRESH_DELAY * 2) / 1000);
|
||||
myTimeout = OSUtils::now() + 10000;
|
||||
do {
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(natpmp.s, &fds);
|
||||
getnatpmprequesttimeout(&natpmp, &timeout);
|
||||
select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
|
||||
r = readnatpmpresponseorretry(&natpmp, &response);
|
||||
if (OSUtils::now() >= myTimeout)
|
||||
break;
|
||||
} while (r == NATPMP_TRYAGAIN);
|
||||
if (r == 0) {
|
||||
publicAddress.setPort(response.pnu.newportmapping.mappedpublicport);
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: NAT-PMP: mapped %u to %s"ZT_EOL_S,(unsigned int)localPort,publicAddress.toString().c_str());
|
||||
#endif
|
||||
Mutex::Lock sl(surface_l);
|
||||
surface.clear();
|
||||
surface.push_back(publicAddress);
|
||||
natPmpSuccess = true;
|
||||
closenatpmp(&natpmp);
|
||||
break;
|
||||
} else {
|
||||
closenatpmp(&natpmp);
|
||||
// continue
|
||||
}
|
||||
}
|
||||
|
||||
if (!natPmpSuccess) {
|
||||
mode = 1;
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: NAT-PMP: request failed, switching to UPnP mode"ZT_EOL_S);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// UPnP mode
|
||||
// ---------------------------------------------------------------------
|
||||
if (mode == 1) {
|
||||
char lanaddr[4096];
|
||||
char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P
|
||||
char inport[16];
|
||||
char outport[16];
|
||||
struct UPNPUrls urls;
|
||||
struct IGDdatas data;
|
||||
|
||||
int upnpError = 0;
|
||||
UPNPDev *devlist = upnpDiscoverAll(5000,(const char *)0,(const char *)0,0,0,2,&upnpError);
|
||||
if (devlist) {
|
||||
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
{
|
||||
UPNPDev *dev = devlist;
|
||||
while (dev) {
|
||||
fprintf(stderr,"PortMapper: found UPnP device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st);
|
||||
dev = dev->pNext;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(lanaddr,0,sizeof(lanaddr));
|
||||
memset(externalip,0,sizeof(externalip));
|
||||
memset(&urls,0,sizeof(urls));
|
||||
memset(&data,0,sizeof(data));
|
||||
Utils::snprintf(inport,sizeof(inport),"%d",localPort);
|
||||
|
||||
if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: my LAN IP address: %s"ZT_EOL_S,lanaddr);
|
||||
#endif
|
||||
if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) {
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: my external IP address: %s"ZT_EOL_S,externalip);
|
||||
#endif
|
||||
|
||||
for(int tries=0;tries<60;++tries) {
|
||||
int tryPort = (int)localPort + tries;
|
||||
if (tryPort >= 65535)
|
||||
tryPort = (tryPort - 65535) + 1025;
|
||||
Utils::snprintf(outport,sizeof(outport),"%u",tryPort);
|
||||
|
||||
// First check and see if this port is already mapped to the
|
||||
// same unique name. If so, keep this mapping and don't try
|
||||
// to map again since this can break buggy routers. But don't
|
||||
// fail if this command fails since not all routers support it.
|
||||
{
|
||||
char haveIntClient[128]; // 128 == big enough for all these as per miniupnpc "documentation"
|
||||
char haveIntPort[128];
|
||||
char haveDesc[128];
|
||||
char haveEnabled[128];
|
||||
char haveLeaseDuration[128];
|
||||
memset(haveIntClient,0,sizeof(haveIntClient));
|
||||
memset(haveIntPort,0,sizeof(haveIntPort));
|
||||
memset(haveDesc,0,sizeof(haveDesc));
|
||||
memset(haveEnabled,0,sizeof(haveEnabled));
|
||||
memset(haveLeaseDuration,0,sizeof(haveLeaseDuration));
|
||||
if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) {
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: reusing previously reserved external port: %s"ZT_EOL_S,outport);
|
||||
#endif
|
||||
Mutex::Lock sl(surface_l);
|
||||
surface.clear();
|
||||
InetAddress tmp(externalip);
|
||||
tmp.setPort(tryPort);
|
||||
surface.push_back(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to map this port
|
||||
int mapResult = 0;
|
||||
if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) {
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: reserved external port: %s"ZT_EOL_S,outport);
|
||||
#endif
|
||||
Mutex::Lock sl(surface_l);
|
||||
surface.clear();
|
||||
InetAddress tmp(externalip);
|
||||
tmp.setPort(tryPort);
|
||||
surface.push_back(tmp);
|
||||
break;
|
||||
} else {
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult);
|
||||
#endif
|
||||
Thread::sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
mode = 0;
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode"ZT_EOL_S);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
mode = 0;
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode"ZT_EOL_S);
|
||||
#endif
|
||||
}
|
||||
|
||||
freeUPNPDevlist(devlist);
|
||||
|
||||
} else {
|
||||
mode = 0;
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d"ZT_EOL_S,upnpError);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
#ifdef ZT_PORTMAPPER_TRACE
|
||||
fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
|
||||
#endif
|
||||
Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
volatile bool run;
|
||||
int localPort;
|
||||
std::string uniqueName;
|
||||
|
||||
Mutex surface_l;
|
||||
std::vector<InetAddress> surface;
|
||||
};
|
||||
|
||||
PortMapper::PortMapper(int localUdpPortToMap,const char *uniqueName)
|
||||
{
|
||||
_impl = new PortMapperImpl(localUdpPortToMap,uniqueName);
|
||||
Thread::start(_impl);
|
||||
}
|
||||
|
||||
PortMapper::~PortMapper()
|
||||
{
|
||||
_impl->run = false;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> PortMapper::get() const
|
||||
{
|
||||
Mutex::Lock _l(_impl->surface_l);
|
||||
return _impl->surface;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_USE_MINIUPNPC
|
71
osdep/PortMapper.hpp
Normal file
71
osdep/PortMapper.hpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef ZT_USE_MINIUPNPC
|
||||
|
||||
#ifndef ZT_PORTMAPPER_HPP
|
||||
#define ZT_PORTMAPPER_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
/**
|
||||
* How frequently should we refresh our UPNP/NAT-PnP/whatever state?
|
||||
*/
|
||||
#define ZT_PORTMAPPER_REFRESH_DELAY 300000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class PortMapperImpl;
|
||||
|
||||
/**
|
||||
* UPnP/NAT-PnP port mapping "daemon"
|
||||
*/
|
||||
class PortMapper
|
||||
{
|
||||
friend class PortMapperImpl;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create and start port mapper service
|
||||
*
|
||||
* @param localUdpPortToMap Port we want visible to the outside world
|
||||
* @param name Unique name of this endpoint (based on ZeroTier address)
|
||||
*/
|
||||
PortMapper(int localUdpPortToMap,const char *uniqueName);
|
||||
|
||||
~PortMapper();
|
||||
|
||||
/**
|
||||
* @return All current external mappings for our port
|
||||
*/
|
||||
std::vector<InetAddress> get() const;
|
||||
|
||||
private:
|
||||
PortMapperImpl *_impl;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ZT_USE_MINIUPNPC
|
6
osdep/README.md
Normal file
6
osdep/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
OS-Dependent and OS-Interface Things
|
||||
======
|
||||
|
||||
This folder contains stuff that interfaces with the base operating system
|
||||
like Phy for network access and the various OS-specific Ethernet tap
|
||||
drivers.
|
194
osdep/Thread.hpp
Normal file
194
osdep/Thread.hpp
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_THREAD_HPP
|
||||
#define ZT_THREAD_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <string.h>
|
||||
#include "../node/Mutex.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
template<typename C>
|
||||
static DWORD WINAPI ___zt_threadMain(LPVOID lpParam)
|
||||
{
|
||||
try {
|
||||
((C *)lpParam)->threadMain();
|
||||
} catch ( ... ) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
Thread()
|
||||
throw()
|
||||
{
|
||||
_th = NULL;
|
||||
_tid = 0;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
static inline Thread start(C *instance)
|
||||
throw(std::runtime_error)
|
||||
{
|
||||
Thread t;
|
||||
t._th = CreateThread(NULL,0,&___zt_threadMain<C>,(LPVOID)instance,0,&t._tid);
|
||||
if (t._th == NULL)
|
||||
throw std::runtime_error("CreateThread() failed");
|
||||
return t;
|
||||
}
|
||||
|
||||
static inline void join(const Thread &t)
|
||||
{
|
||||
if (t._th != NULL) {
|
||||
for(;;) {
|
||||
DWORD ec = STILL_ACTIVE;
|
||||
GetExitCodeThread(t._th,&ec);
|
||||
if (ec == STILL_ACTIVE)
|
||||
WaitForSingleObject(t._th,1000);
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sleep(unsigned long ms)
|
||||
{
|
||||
Sleep((DWORD)ms);
|
||||
}
|
||||
|
||||
// Not available on *nix platforms
|
||||
static inline void cancelIO(const Thread &t)
|
||||
{
|
||||
if (t._th != NULL)
|
||||
CancelSynchronousIo(t._th);
|
||||
}
|
||||
|
||||
inline operator bool() const throw() { return (_th != NULL); }
|
||||
|
||||
private:
|
||||
HANDLE _th;
|
||||
DWORD _tid;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
template<typename C>
|
||||
static void *___zt_threadMain(void *instance)
|
||||
{
|
||||
try {
|
||||
((C *)instance)->threadMain();
|
||||
} catch ( ... ) {}
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread identifier, and static methods to start and join threads
|
||||
*/
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
Thread()
|
||||
throw()
|
||||
{
|
||||
memset(&_tid,0,sizeof(_tid));
|
||||
_started = false;
|
||||
}
|
||||
|
||||
Thread(const Thread &t)
|
||||
throw()
|
||||
{
|
||||
memcpy(&_tid,&(t._tid),sizeof(_tid));
|
||||
_started = t._started;
|
||||
}
|
||||
|
||||
inline Thread &operator=(const Thread &t)
|
||||
throw()
|
||||
{
|
||||
memcpy(&_tid,&(t._tid),sizeof(_tid));
|
||||
_started = t._started;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new thread
|
||||
*
|
||||
* @param instance Instance whose threadMain() method gets called by new thread
|
||||
* @return Thread identifier
|
||||
* @throws std::runtime_error Unable to create thread
|
||||
* @tparam C Class containing threadMain()
|
||||
*/
|
||||
template<typename C>
|
||||
static inline Thread start(C *instance)
|
||||
throw(std::runtime_error)
|
||||
{
|
||||
Thread t;
|
||||
t._started = true;
|
||||
if (pthread_create(&t._tid,(const pthread_attr_t *)0,&___zt_threadMain<C>,instance))
|
||||
throw std::runtime_error("pthread_create() failed, unable to create thread");
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join to a thread, waiting for it to terminate (does nothing on null Thread values)
|
||||
*
|
||||
* @param t Thread to join
|
||||
*/
|
||||
static inline void join(const Thread &t)
|
||||
{
|
||||
if (t._started)
|
||||
pthread_join(t._tid,(void **)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep the current thread
|
||||
*
|
||||
* @param ms Number of milliseconds to sleep
|
||||
*/
|
||||
static inline void sleep(unsigned long ms) { usleep(ms * 1000); }
|
||||
|
||||
inline operator bool() const throw() { return (_started); }
|
||||
|
||||
private:
|
||||
pthread_t _tid;
|
||||
volatile bool _started;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // __WINDOWS__ / !__WINDOWS__
|
||||
|
||||
#endif
|
1210
osdep/WindowsEthernetTap.cpp
Normal file
1210
osdep/WindowsEthernetTap.cpp
Normal file
File diff suppressed because it is too large
Load diff
150
osdep/WindowsEthernetTap.hpp
Normal file
150
osdep/WindowsEthernetTap.hpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_WINDOWSETHERNETTAP_HPP
|
||||
#define ZT_WINDOWSETHERNETTAP_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ifdef.h>
|
||||
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "../node/Array.hpp"
|
||||
#include "../node/MulticastGroup.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../osdep/Thread.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class WindowsEthernetTap
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Installs a new instance of the ZT tap driver
|
||||
*
|
||||
* @param pathToInf Path to zttap driver .inf file
|
||||
* @param deviceInstanceId Buffer to fill with device instance ID on success (and if SetupDiGetDeviceInstanceIdA succeeds, which it should)
|
||||
* @return Empty string on success, otherwise an error message
|
||||
*/
|
||||
static std::string addNewPersistentTapDevice(const char *pathToInf,std::string &deviceInstanceId);
|
||||
|
||||
/**
|
||||
* Uninstalls all persistent tap devices that have legacy drivers
|
||||
*
|
||||
* @return Empty string on success, otherwise an error message
|
||||
*/
|
||||
static std::string destroyAllLegacyPersistentTapDevices();
|
||||
|
||||
/**
|
||||
* Uninstalls all persistent tap devices on the system
|
||||
*
|
||||
* @return Empty string on success, otherwise an error message
|
||||
*/
|
||||
static std::string destroyAllPersistentTapDevices();
|
||||
|
||||
/**
|
||||
* Uninstall a specific persistent tap device by instance ID
|
||||
*
|
||||
* @param instanceId Device instance ID
|
||||
* @return Empty string on success, otherwise an error message
|
||||
*/
|
||||
static std::string deletePersistentTapDevice(const char *instanceId);
|
||||
|
||||
/**
|
||||
* Disable a persistent tap device by instance ID
|
||||
*
|
||||
* @param instanceId Device instance ID
|
||||
* @param enabled Enable device?
|
||||
* @return True if device was found and disabled
|
||||
*/
|
||||
static bool setPersistentTapDeviceState(const char *instanceId,bool enabled);
|
||||
|
||||
WindowsEthernetTap(
|
||||
const char *hp,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg);
|
||||
|
||||
~WindowsEthernetTap();
|
||||
|
||||
void setEnabled(bool en);
|
||||
bool enabled() const;
|
||||
bool addIp(const InetAddress &ip);
|
||||
bool removeIp(const InetAddress &ip);
|
||||
std::vector<InetAddress> ips() const;
|
||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||
std::string deviceName() const;
|
||||
void setFriendlyName(const char *friendlyName);
|
||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
||||
|
||||
inline const NET_LUID &luid() const { return _deviceLuid; }
|
||||
inline const GUID &guid() const { return _deviceGuid; }
|
||||
inline const std::string &instanceId() const { return _deviceInstanceId; }
|
||||
NET_IFINDEX interfaceIndex() const;
|
||||
|
||||
void threadMain()
|
||||
throw();
|
||||
|
||||
private:
|
||||
NET_IFINDEX _getDeviceIndex(); // throws on failure
|
||||
std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
|
||||
void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
|
||||
void _syncIps();
|
||||
|
||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
||||
void *_arg;
|
||||
MAC _mac;
|
||||
uint64_t _nwid;
|
||||
Thread _thread;
|
||||
|
||||
volatile HANDLE _tap;
|
||||
HANDLE _injectSemaphore;
|
||||
|
||||
GUID _deviceGuid;
|
||||
NET_LUID _deviceLuid;
|
||||
std::string _netCfgInstanceId;
|
||||
std::string _deviceInstanceId;
|
||||
|
||||
std::vector<InetAddress> _assignedIps; // IPs assigned with addIp
|
||||
Mutex _assignedIps_m;
|
||||
|
||||
std::vector<MulticastGroup> _multicastGroups;
|
||||
|
||||
std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
|
||||
Mutex _injectPending_m;
|
||||
|
||||
std::string _pathToHelpers;
|
||||
|
||||
volatile bool _run;
|
||||
volatile bool _initialized;
|
||||
volatile bool _enabled;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue