mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-20 09:46:13 +00:00
Several things:
(1) Probable fix for issue #7 and major cleanup of EthernetTap code with consolidation for all unix-like systems and specialization for different flavors only when needed. (2) Refactor of Buffer<> to make its members private, and Packet to use Buffer's methods exclusively to access them. This improves clarity and means we're no longer lying about Buffer's role in the code's security posture. (3) Add -fstack-protect to Makefile to bounds check stack variables.
This commit is contained in:
parent
41cd980bf7
commit
ef3e319c64
@ -5,7 +5,7 @@ INCLUDES=-Iext/bin/libcrypto/include -Iext/jsoncpp/include
|
|||||||
DEFS=-DZT_ARCH="x86_combined" -DZT_OSNAME="mac" -DZT_TRACE
|
DEFS=-DZT_ARCH="x86_combined" -DZT_OSNAME="mac" -DZT_TRACE
|
||||||
|
|
||||||
# Uncomment for a release optimized universal binary build
|
# Uncomment for a release optimized universal binary build
|
||||||
CFLAGS=-arch i386 -arch x86_64 -Wall -O3 -ftree-vectorize -pthread -mmacosx-version-min=10.6 -DNDEBUG $(INCLUDES) $(DEFS)
|
CFLAGS=-arch i386 -arch x86_64 -Wall -O3 -ftree-vectorize -fstack-protector -pthread -mmacosx-version-min=10.6 -DNDEBUG $(INCLUDES) $(DEFS)
|
||||||
STRIP=strip
|
STRIP=strip
|
||||||
|
|
||||||
# Uncomment for a debug build
|
# Uncomment for a debug build
|
||||||
|
@ -388,7 +388,7 @@ public:
|
|||||||
return !(*this < b);
|
return !(*this < b);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
unsigned int _l;
|
unsigned int _l;
|
||||||
char ZT_VAR_MAY_ALIAS _b[C];
|
char ZT_VAR_MAY_ALIAS _b[C];
|
||||||
};
|
};
|
||||||
|
@ -28,10 +28,55 @@
|
|||||||
#ifndef _ZT_CONSTANTS_HPP
|
#ifndef _ZT_CONSTANTS_HPP
|
||||||
#define _ZT_CONSTANTS_HPP
|
#define _ZT_CONSTANTS_HPP
|
||||||
|
|
||||||
// Assume these are little-endian, since we don't support old PPC MACs
|
//
|
||||||
// and all newer Mac or Windows systems are either x86_32, x86_64, or
|
// This include file also auto-detects and canonicalizes some environment
|
||||||
// ARM in little-endian mode.
|
// information defines:
|
||||||
#if defined(__APPLE__) || defined(_WIN32)
|
//
|
||||||
|
// __LINUX__
|
||||||
|
// __APPLE__
|
||||||
|
// __UNIX_LIKE__ - any "unix like" OS (BSD, posix, etc.)
|
||||||
|
// __WINDOWS__
|
||||||
|
//
|
||||||
|
// Also makes sure __BYTE_ORDER is defined reasonably.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Canonicalize Linux... is this necessary? Do it anyway to be defensive.
|
||||||
|
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
||||||
|
#ifndef __LINUX__
|
||||||
|
#define __LINUX__
|
||||||
|
#ifndef __UNIX_LIKE__
|
||||||
|
#define __UNIX_LIKE__
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: Android is what? Linux technically, but does it define it?
|
||||||
|
|
||||||
|
// OSX and iOS are unix-like OSes far as we're concerned
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#ifndef __UNIX_LIKE__
|
||||||
|
#define __UNIX_LIKE__
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Linux has endian.h
|
||||||
|
#ifdef __LINUX__
|
||||||
|
#include <endian.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#ifndef __WINDOWS__
|
||||||
|
#define __WINDOWS__
|
||||||
|
#endif
|
||||||
|
#undef __UNIX_LIKE__
|
||||||
|
#define ZT_PATH_SEPARATOR '\\'
|
||||||
|
#define ZT_PATH_SEPARATOR_S "\\"
|
||||||
|
#define ZT_EOL_S "\r\n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Assume these are little-endian. PPC is not supported for OSX, and ARM
|
||||||
|
// runs in little-endian mode for these OS families.
|
||||||
|
#if defined(__APPLE__) || defined(__WINDOWS__)
|
||||||
#undef __BYTE_ORDER
|
#undef __BYTE_ORDER
|
||||||
#undef __LITTLE_ENDIAN
|
#undef __LITTLE_ENDIAN
|
||||||
#undef __BIG_ENDIAN
|
#undef __BIG_ENDIAN
|
||||||
@ -40,33 +85,23 @@
|
|||||||
#define __BYTE_ORDER 1234
|
#define __BYTE_ORDER 1234
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Linux has endian.h, which should tell us
|
#ifdef __UNIX_LIKE__
|
||||||
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
|
||||||
#include <endian.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __BYTE_ORDER
|
|
||||||
error_no_byte_order_defined
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ZT_OSNAME
|
|
||||||
error_no_ZT_OSNAME
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ZT_ARCH
|
|
||||||
error_no_ZT_ARCH
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define ZT_PATH_SEPARATOR '\\'
|
|
||||||
#define ZT_PATH_SEPARATOR_S "\\"
|
|
||||||
#define ZT_EOL_S "\r\n"
|
|
||||||
#else
|
|
||||||
#define ZT_PATH_SEPARATOR '/'
|
#define ZT_PATH_SEPARATOR '/'
|
||||||
#define ZT_PATH_SEPARATOR_S "/"
|
#define ZT_PATH_SEPARATOR_S "/"
|
||||||
#define ZT_EOL_S "\n"
|
#define ZT_EOL_S "\n"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Error out if required symbols are missing
|
||||||
|
#ifndef __BYTE_ORDER
|
||||||
|
error_no_byte_order_defined;
|
||||||
|
#endif
|
||||||
|
#ifndef ZT_OSNAME
|
||||||
|
error_no_ZT_OSNAME_defined;
|
||||||
|
#endif
|
||||||
|
#ifndef ZT_ARCH
|
||||||
|
error_no_ZT_ARCH_defined;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Length of a ZeroTier address in bytes
|
* Length of a ZeroTier address in bytes
|
||||||
*/
|
*/
|
||||||
|
@ -30,15 +30,17 @@
|
|||||||
#include "EthernetTap.hpp"
|
#include "EthernetTap.hpp"
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
#include "RuntimeEnvironment.hpp"
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "MulticastGroup.hpp"
|
|
||||||
|
|
||||||
// ff:ff:ff:ff:ff:ff with no ADI
|
// ff:ff:ff:ff:ff:ff with no ADI
|
||||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
||||||
|
|
||||||
/* ======================================================================== */
|
//
|
||||||
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
// TAP implementation for *nix OSes, with some specialization for different flavors
|
||||||
/* ======================================================================== */
|
//
|
||||||
|
|
||||||
|
#ifdef __UNIX_LIKE__ /////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -52,10 +54,13 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <net/if_arp.h>
|
#include <net/if_arp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#ifdef __LINUX__
|
||||||
|
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
#include <linux/if_tun.h>
|
#include <linux/if_tun.h>
|
||||||
#include <linux/if_addr.h>
|
#include <linux/if_addr.h>
|
||||||
@ -64,23 +69,49 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
|
|||||||
#define ZT_ETHERTAP_IP_COMMAND "/sbin/ip"
|
#define ZT_ETHERTAP_IP_COMMAND "/sbin/ip"
|
||||||
#define ZT_ETHERTAP_SYSCTL_COMMAND "/sbin/sysctl"
|
#define ZT_ETHERTAP_SYSCTL_COMMAND "/sbin/sysctl"
|
||||||
|
|
||||||
|
#endif // __LINUX__
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
|
||||||
|
#define ZT_ETHERTAP_IFCONFIG "/sbin/ifconfig"
|
||||||
|
#define ZT_MAC_KEXTLOAD "/sbin/kextload"
|
||||||
|
#define ZT_MAC_IPCONFIG "/usr/sbin/ipconfig"
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
// Only permit one tap to be opened concurrently across the entire process
|
||||||
static Mutex __tapCreateLock;
|
static Mutex __tapCreateLock;
|
||||||
|
|
||||||
EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
|
#ifdef __LINUX__
|
||||||
|
EthernetTap::EthernetTap(
|
||||||
|
const RuntimeEnvironment *renv,
|
||||||
|
const MAC &mac,
|
||||||
|
unsigned int mtu,
|
||||||
|
void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
|
||||||
|
void *arg)
|
||||||
throw(std::runtime_error) :
|
throw(std::runtime_error) :
|
||||||
_mac(mac),
|
_mac(mac),
|
||||||
_mtu(mtu),
|
_mtu(mtu),
|
||||||
_r(renv),
|
_r(renv),
|
||||||
_putBuf((unsigned char *)0),
|
_handler(handler),
|
||||||
_getBuf((unsigned char *)0),
|
_arg(arg),
|
||||||
_fd(0),
|
_fd(0)
|
||||||
_isReading(false)
|
|
||||||
{
|
{
|
||||||
char procpath[128];
|
char procpath[128];
|
||||||
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
|
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
|
||||||
|
|
||||||
|
if (mtu > 4096)
|
||||||
|
throw std::runtime_error("max tap MTU is 4096");
|
||||||
|
|
||||||
_fd = ::open("/dev/net/tun",O_RDWR);
|
_fd = ::open("/dev/net/tun",O_RDWR);
|
||||||
if (_fd <= 0)
|
if (_fd <= 0)
|
||||||
throw std::runtime_error("could not open TUN/TAP device");
|
throw std::runtime_error("could not open TUN/TAP device");
|
||||||
@ -152,36 +183,135 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
|
|||||||
|
|
||||||
::close(sock);
|
::close(sock);
|
||||||
|
|
||||||
_putBuf = new unsigned char[((mtu + 16) * 2)];
|
::pipe(_shutdownSignalPipe);
|
||||||
_getBuf = _putBuf + (mtu + 16);
|
|
||||||
|
|
||||||
TRACE("tap %s created",_dev);
|
TRACE("tap %s created",_dev);
|
||||||
|
|
||||||
|
start();
|
||||||
}
|
}
|
||||||
|
#endif // __LINUX__
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
EthernetTap::EthernetTap(
|
||||||
|
const RuntimeEnvironment *renv,
|
||||||
|
const MAC &mac,
|
||||||
|
unsigned int mtu,
|
||||||
|
void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
|
||||||
|
void *arg)
|
||||||
|
throw(std::runtime_error) :
|
||||||
|
_mac(mac),
|
||||||
|
_mtu(mtu),
|
||||||
|
_r(renv),
|
||||||
|
_handler(handler),
|
||||||
|
_arg(arg),
|
||||||
|
_fd(0)
|
||||||
|
{
|
||||||
|
char devpath[64],ethaddr[64],mtustr[16];
|
||||||
|
struct stat tmp;
|
||||||
|
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
|
||||||
|
|
||||||
|
if (mtu > 4096)
|
||||||
|
throw std::runtime_error("max tap MTU is 4096");
|
||||||
|
|
||||||
|
// Check for existence of ZT tap devices, try to load module if not there
|
||||||
|
if (stat("/dev/zt0",&tmp)) {
|
||||||
|
int kextpid;
|
||||||
|
char tmp[4096];
|
||||||
|
strcpy(tmp,_r->homePath.c_str());
|
||||||
|
if ((kextpid = (int)fork()) == 0) {
|
||||||
|
chdir(tmp);
|
||||||
|
execl(ZT_MAC_KEXTLOAD,ZT_MAC_KEXTLOAD,"-q","-repository",tmp,"tap.kext",(const char *)0);
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
int exitcode = -1;
|
||||||
|
waitpid(kextpid,&exitcode,0);
|
||||||
|
usleep(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stat("/dev/zt0",&tmp))
|
||||||
|
throw std::runtime_error("/dev/zt# tap devices do not exist and unable to load kernel extension");
|
||||||
|
|
||||||
|
// Open the first available device (ones in use will fail with resource busy)
|
||||||
|
for(int i=0;i<256;++i) {
|
||||||
|
sprintf(devpath,"/dev/zt%d",i);
|
||||||
|
if (stat(devpath,&tmp))
|
||||||
|
throw std::runtime_error("no more TAP devices available");
|
||||||
|
_fd = ::open(devpath,O_RDWR);
|
||||||
|
if (_fd > 0) {
|
||||||
|
sprintf(_dev,"zt%d",i);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(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]);
|
||||||
|
sprintf(mtustr,"%u",mtu);
|
||||||
|
|
||||||
|
// Configure MAC address and MTU, bring interface up
|
||||||
|
long cpid;
|
||||||
|
if ((cpid = (long)fork()) == 0) {
|
||||||
|
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
whack(); // turns on IPv6 on OSX
|
||||||
|
|
||||||
|
::pipe(_shutdownSignalPipe);
|
||||||
|
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
EthernetTap::~EthernetTap()
|
EthernetTap::~EthernetTap()
|
||||||
{
|
{
|
||||||
this->close();
|
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
||||||
delete [] _putBuf;
|
join();
|
||||||
|
::close(_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
void EthernetTap::whack()
|
void EthernetTap::whack()
|
||||||
{
|
{
|
||||||
// Linux requires nothing here
|
long cpid = (long)fork();
|
||||||
|
if (cpid == 0) {
|
||||||
|
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
int exitcode = -1;
|
||||||
|
waitpid(cpid,&exitcode,0);
|
||||||
|
if (exitcode) {
|
||||||
|
LOG("%s: ipconfig set AUTOMATIC-V6 failed",_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
void EthernetTap::whack() {}
|
||||||
|
#endif // __APPLE__ / !__APPLE__
|
||||||
|
|
||||||
static bool ___removeIp(const char *_dev,std::set<InetAddress> &_ips,const InetAddress &ip)
|
#ifdef __LINUX__
|
||||||
|
static bool ___removeIp(const char *_dev,const InetAddress &ip)
|
||||||
{
|
{
|
||||||
long cpid;
|
long cpid = (long)fork();
|
||||||
if ((cpid = (long)fork()) == 0) {
|
if (cpid == 0) {
|
||||||
execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
|
execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
|
||||||
exit(1); /* not reached unless exec fails */
|
exit(1); /* not reached unless exec fails */
|
||||||
} else {
|
} else {
|
||||||
int exitcode = 1;
|
int exitcode = 1;
|
||||||
waitpid(cpid,&exitcode,0);
|
waitpid(cpid,&exitcode,0);
|
||||||
if (exitcode == 0) {
|
return (exitcode == 0);
|
||||||
_ips.erase(ip);
|
|
||||||
return true;
|
|
||||||
} else return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,13 +327,17 @@ bool EthernetTap::addIP(const InetAddress &ip)
|
|||||||
// Remove and reconfigure if address is the same but netmask is different
|
// Remove and reconfigure if address is the same but netmask is different
|
||||||
for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
|
for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
|
||||||
if (i->ipsEqual(ip)) {
|
if (i->ipsEqual(ip)) {
|
||||||
___removeIp(_dev,_ips,*i);
|
if (___removeIp(_dev,*i)) {
|
||||||
break;
|
_ips.erase(i);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int cpid;
|
long cpid;
|
||||||
if ((cpid = (int)fork()) == 0) {
|
if ((cpid = (long)fork()) == 0) {
|
||||||
execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
|
execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
} else {
|
} else {
|
||||||
@ -217,97 +351,101 @@ bool EthernetTap::addIP(const InetAddress &ip)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif // __LINUX__
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
static bool ___removeIp(const char *_dev,const InetAddress &ip)
|
||||||
|
{
|
||||||
|
int cpid;
|
||||||
|
if ((cpid = (int)fork()) == 0) {
|
||||||
|
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
int exitcode = -1;
|
||||||
|
waitpid(cpid,&exitcode,0);
|
||||||
|
return (exitcode == 0);
|
||||||
|
}
|
||||||
|
return false; // never reached, make compiler shut up about return value
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthernetTap::addIP(const InetAddress &ip)
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_ips_m);
|
||||||
|
|
||||||
|
if (!ip)
|
||||||
|
return false;
|
||||||
|
if (_ips.count(ip) > 0)
|
||||||
|
return true; // IP/netmask already assigned
|
||||||
|
|
||||||
|
// Remove and reconfigure if address is the same but netmask is different
|
||||||
|
for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
|
||||||
|
if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) {
|
||||||
|
if (___removeIp(_dev,*i)) {
|
||||||
|
_ips.erase(i);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpid;
|
||||||
|
if ((cpid = (int)fork()) == 0) {
|
||||||
|
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
int exitcode = -1;
|
||||||
|
waitpid(cpid,&exitcode,0);
|
||||||
|
if (exitcode == 0) {
|
||||||
|
_ips.insert(ip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
bool EthernetTap::removeIP(const InetAddress &ip)
|
bool EthernetTap::removeIP(const InetAddress &ip)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_ips_m);
|
Mutex::Lock _l(_ips_m);
|
||||||
if (_ips.count(ip) > 0)
|
if (_ips.count(ip) > 0) {
|
||||||
return ___removeIp(_dev,_ips,ip);
|
if (___removeIp(_dev,ip)) {
|
||||||
|
_ips.erase(ip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
||||||
{
|
{
|
||||||
|
char putBuf[4096 + 14];
|
||||||
if ((_fd > 0)&&(len <= _mtu)) {
|
if ((_fd > 0)&&(len <= _mtu)) {
|
||||||
for(int i=0;i<6;++i)
|
for(int i=0;i<6;++i)
|
||||||
_putBuf[i] = to.data[i];
|
putBuf[i] = to.data[i];
|
||||||
for(int i=0;i<6;++i)
|
for(int i=0;i<6;++i)
|
||||||
_putBuf[i+6] = from.data[i];
|
putBuf[i+6] = from.data[i];
|
||||||
*((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
|
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
|
||||||
memcpy(_putBuf + 14,data,len);
|
memcpy(putBuf + 14,data,len);
|
||||||
::write(_fd,_putBuf,len + 14);
|
len += 14;
|
||||||
}
|
int n = ::write(_fd,putBuf,len);
|
||||||
}
|
if (n <= 0) {
|
||||||
|
LOG("error writing packet to Ethernet tap device: %s",strerror(errno));
|
||||||
unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int ðerType,void *buf)
|
} else if (n != (int)len) {
|
||||||
{
|
// Saw this gremlin once, so log it if we see it again... OSX tap
|
||||||
for(;;) {
|
// or something seems to have goofy issues with certain MTUs.
|
||||||
if (_fd > 0) {
|
LOG("ERROR: write underrun: %s tap write() wrote %d of %u bytes of frame",_dev,n,len);
|
||||||
_isReading_m.lock();
|
|
||||||
_isReading = true;
|
|
||||||
_isReadingThreadId = pthread_self();
|
|
||||||
_isReading_m.unlock();
|
|
||||||
|
|
||||||
int n = (int)::read(_fd,_getBuf,_mtu + 14);
|
|
||||||
|
|
||||||
_isReading_m.lock();
|
|
||||||
_isReading = false;
|
|
||||||
_isReading_m.unlock();
|
|
||||||
|
|
||||||
if (n > 14) {
|
|
||||||
for(int i=0;i<6;++i)
|
|
||||||
to.data[i] = _getBuf[i];
|
|
||||||
for(int i=0;i<6;++i)
|
|
||||||
from.data[i] = _getBuf[i + 6];
|
|
||||||
etherType = ntohs(((uint16_t *)_getBuf)[6]);
|
|
||||||
n -= 14;
|
|
||||||
memcpy(buf,_getBuf + 14,n);
|
|
||||||
return (unsigned int)n;
|
|
||||||
} else if (n < 0) {
|
|
||||||
if (_fd <= 0)
|
|
||||||
break;
|
|
||||||
else if ((errno == EINTR)||(errno == ETIMEDOUT))
|
|
||||||
continue;
|
|
||||||
else {
|
|
||||||
TRACE("unexpected error reading from tap: %s",strerror(errno));
|
|
||||||
::close(_fd);
|
|
||||||
_fd = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("incomplete read from tap: %d bytes",n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EthernetTap::deviceName()
|
std::string EthernetTap::deviceName() const
|
||||||
{
|
{
|
||||||
return std::string(_dev);
|
return std::string(_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EthernetTap::open() const
|
#ifdef __LINUX__
|
||||||
{
|
|
||||||
return (_fd > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EthernetTap::close()
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(__tapCreateLock); // also prevent create during close()
|
|
||||||
if (_fd > 0) {
|
|
||||||
int f = _fd;
|
|
||||||
_fd = 0;
|
|
||||||
::close(f);
|
|
||||||
|
|
||||||
_isReading_m.lock();
|
|
||||||
if (_isReading)
|
|
||||||
pthread_kill(_isReadingThreadId,SIGUSR2);
|
|
||||||
_isReading_m.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
||||||
{
|
{
|
||||||
char *ptr,*ptr2;
|
char *ptr,*ptr2;
|
||||||
@ -363,290 +501,9 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
|||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
#endif __LINUX__
|
||||||
|
|
||||||
} // namespace ZeroTier
|
#ifdef __APPLE__
|
||||||
|
|
||||||
/* ======================================================================== */
|
|
||||||
#elif defined(__APPLE__) /* ----------------------------------------------- */
|
|
||||||
/* ======================================================================== */
|
|
||||||
|
|
||||||
#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/uio.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <net/if_dl.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
|
|
||||||
#define ZT_ETHERTAP_IFCONFIG "/sbin/ifconfig"
|
|
||||||
#define ZT_MAC_KEXTLOAD "/sbin/kextload"
|
|
||||||
#define ZT_MAC_IPCONFIG "/usr/sbin/ipconfig"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
static Mutex __tapCreateLock;
|
|
||||||
|
|
||||||
EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
|
|
||||||
throw(std::runtime_error) :
|
|
||||||
_mac(mac),
|
|
||||||
_mtu(mtu),
|
|
||||||
_r(renv),
|
|
||||||
_putBuf((unsigned char *)0),
|
|
||||||
_getBuf((unsigned char *)0),
|
|
||||||
_fd(0),
|
|
||||||
_isReading(false)
|
|
||||||
{
|
|
||||||
char devpath[64],ethaddr[64],mtustr[16];
|
|
||||||
struct stat tmp;
|
|
||||||
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
|
|
||||||
|
|
||||||
// Check for existence of ZT tap devices, try to load module if not there
|
|
||||||
if (stat("/dev/zt0",&tmp)) {
|
|
||||||
int kextpid;
|
|
||||||
char tmp[4096];
|
|
||||||
strcpy(tmp,_r->homePath.c_str());
|
|
||||||
if ((kextpid = (int)fork()) == 0) {
|
|
||||||
chdir(tmp);
|
|
||||||
execl(ZT_MAC_KEXTLOAD,ZT_MAC_KEXTLOAD,"-q","-repository",tmp,"tap.kext",(const char *)0);
|
|
||||||
exit(-1);
|
|
||||||
} else {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(kextpid,&exitcode,0);
|
|
||||||
usleep(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stat("/dev/zt0",&tmp))
|
|
||||||
throw std::runtime_error("/dev/zt# tap devices do not exist and unable to load kernel extension");
|
|
||||||
|
|
||||||
// Open the first available device (ones in use will fail with resource busy)
|
|
||||||
for(int i=0;i<256;++i) {
|
|
||||||
sprintf(devpath,"/dev/zt%d",i);
|
|
||||||
if (stat(devpath,&tmp))
|
|
||||||
throw std::runtime_error("no more TAP devices available");
|
|
||||||
_fd = ::open(devpath,O_RDWR);
|
|
||||||
if (_fd > 0) {
|
|
||||||
sprintf(_dev,"zt%d",i);
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(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]);
|
|
||||||
sprintf(mtustr,"%u",mtu);
|
|
||||||
|
|
||||||
// Configure MAC address and MTU, bring interface up
|
|
||||||
int cpid;
|
|
||||||
if ((cpid = (int)fork()) == 0) {
|
|
||||||
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
|
|
||||||
exit(-1);
|
|
||||||
} else {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
whack(); // turns on IPv6 on OSX
|
|
||||||
|
|
||||||
_putBuf = new unsigned char[((mtu + 14) * 2)];
|
|
||||||
_getBuf = _putBuf + (mtu + 14);
|
|
||||||
}
|
|
||||||
|
|
||||||
EthernetTap::~EthernetTap()
|
|
||||||
{
|
|
||||||
this->close();
|
|
||||||
delete [] _putBuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EthernetTap::whack()
|
|
||||||
{
|
|
||||||
int cpid = fork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
|
|
||||||
exit(-1);
|
|
||||||
} else {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode) {
|
|
||||||
LOG("%s: ipconfig set AUTOMATIC-V6 failed",_dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to actually remove IP from network device, execs ifconfig
|
|
||||||
static bool ___removeIp(const char *_dev,const InetAddress &ip)
|
|
||||||
{
|
|
||||||
int cpid;
|
|
||||||
if ((cpid = (int)fork()) == 0) {
|
|
||||||
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
|
|
||||||
exit(-1);
|
|
||||||
} else {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
}
|
|
||||||
return false; // never reached, make compiler shut up about return value
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EthernetTap::addIP(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_ips_m);
|
|
||||||
|
|
||||||
if (!ip)
|
|
||||||
return false;
|
|
||||||
if (_ips.count(ip) > 0)
|
|
||||||
return true; // IP/netmask already assigned
|
|
||||||
|
|
||||||
// Remove and reconfigure if address is the same but netmask is different
|
|
||||||
for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
|
|
||||||
if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) {
|
|
||||||
if (___removeIp(_dev,*i)) {
|
|
||||||
_ips.erase(i);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int cpid;
|
|
||||||
if ((cpid = (int)fork()) == 0) {
|
|
||||||
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
|
|
||||||
exit(-1);
|
|
||||||
} else {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode == 0) {
|
|
||||||
_ips.insert(ip);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EthernetTap::removeIP(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_ips_m);
|
|
||||||
if (_ips.count(ip) > 0) {
|
|
||||||
if (___removeIp(_dev,ip)) {
|
|
||||||
_ips.erase(ip);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
|
||||||
{
|
|
||||||
if ((_fd > 0)&&(len <= _mtu)) {
|
|
||||||
for(int i=0;i<6;++i)
|
|
||||||
_putBuf[i] = to.data[i];
|
|
||||||
for(int i=0;i<6;++i)
|
|
||||||
_putBuf[i+6] = from.data[i];
|
|
||||||
*((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
|
|
||||||
memcpy(_putBuf + 14,data,len);
|
|
||||||
len += 14;
|
|
||||||
int n = (int)::write(_fd,_putBuf,len);
|
|
||||||
if (n <= 0) {
|
|
||||||
LOG("error writing packet to Ethernet tap device: %s",strerror(errno));
|
|
||||||
} else if (n != (int)len) {
|
|
||||||
// Saw this gremlin once, so log it if we see it again... OSX tap
|
|
||||||
// or something seems to have goofy issues with certain MTUs.
|
|
||||||
LOG("WARNING: Apple gremlin: tap write() wrote %d of %u bytes of frame",n,len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int ðerType,void *buf)
|
|
||||||
{
|
|
||||||
for(;;) {
|
|
||||||
if (_fd > 0) {
|
|
||||||
_isReading_m.lock();
|
|
||||||
_isReading = true;
|
|
||||||
_isReadingThreadId = pthread_self();
|
|
||||||
_isReading_m.unlock();
|
|
||||||
|
|
||||||
int n = (int)::read(_fd,_getBuf,_mtu + 14);
|
|
||||||
|
|
||||||
_isReading_m.lock();
|
|
||||||
_isReading = false;
|
|
||||||
_isReading_m.unlock();
|
|
||||||
|
|
||||||
if (n > 14) {
|
|
||||||
for(int i=0;i<6;++i)
|
|
||||||
to.data[i] = _getBuf[i];
|
|
||||||
for(int i=0;i<6;++i)
|
|
||||||
from.data[i] = _getBuf[i + 6];
|
|
||||||
etherType = ntohs(((uint16_t *)_getBuf)[6]);
|
|
||||||
n -= 14;
|
|
||||||
memcpy(buf,_getBuf + 14,n);
|
|
||||||
return (unsigned int)n;
|
|
||||||
} else if (n < 0) {
|
|
||||||
if (_fd <= 0)
|
|
||||||
break;
|
|
||||||
else if ((errno == EINTR)||(errno == ETIMEDOUT))
|
|
||||||
continue;
|
|
||||||
else {
|
|
||||||
TRACE("unexpected error reading from tap: %s",strerror(errno));
|
|
||||||
::close(_fd);
|
|
||||||
_fd = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("incomplete read from tap: %d bytes",n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string EthernetTap::deviceName()
|
|
||||||
{
|
|
||||||
return std::string(_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EthernetTap::open() const
|
|
||||||
{
|
|
||||||
return (_fd > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EthernetTap::close()
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(__tapCreateLock); // also prevent create during close()
|
|
||||||
if (_fd > 0) {
|
|
||||||
int f = _fd;
|
|
||||||
_fd = 0;
|
|
||||||
::close(f);
|
|
||||||
|
|
||||||
_isReading_m.lock();
|
|
||||||
if (_isReading)
|
|
||||||
pthread_kill(_isReadingThreadId,SIGUSR2);
|
|
||||||
_isReading_m.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
||||||
{
|
{
|
||||||
std::set<MulticastGroup> newGroups;
|
std::set<MulticastGroup> newGroups;
|
||||||
@ -690,13 +547,54 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
|||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
|
void EthernetTap::main()
|
||||||
|
throw()
|
||||||
|
{
|
||||||
|
fd_set readfds,nullfds;
|
||||||
|
MAC to,from;
|
||||||
|
char getBuf[4096 + 14];
|
||||||
|
Buffer<4096> data;
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_ZERO(&nullfds);
|
||||||
|
int nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
int n = (int)::read(_fd,getBuf,_mtu + 14);
|
||||||
|
|
||||||
|
if (n > 14) {
|
||||||
|
for(int i=0;i<6;++i)
|
||||||
|
to.data[i] = (unsigned char)getBuf[i];
|
||||||
|
for(int i=0;i<6;++i)
|
||||||
|
from.data[i] = (unsigned char)getBuf[i + 6];
|
||||||
|
data.copyFrom(getBuf + 14,(unsigned int)n - 14);
|
||||||
|
_handler(_arg,from,to,ntohs(((const uint16_t *)getBuf)[6]),data);
|
||||||
|
} else if (n < 0) {
|
||||||
|
if ((errno != EINTR)&&(errno != ETIMEDOUT)) {
|
||||||
|
TRACE("unexpected error reading from tap: %s",strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
||||||
/* ======================================================================== */
|
#endif // __UNIX_LIKE__ //////////////////////////////////////////////////////
|
||||||
#elif defined(_WIN32) /* -------------------------------------------------- */
|
|
||||||
/* ======================================================================== */
|
|
||||||
|
|
||||||
/* ======================================================================== */
|
#ifdef __WINDOWS__
|
||||||
#endif
|
|
||||||
/* ======================================================================== */
|
// TODO
|
||||||
|
|
||||||
|
#endif // __WINDOWS__
|
||||||
|
@ -36,14 +36,13 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "Array.hpp"
|
|
||||||
#include "Utils.hpp"
|
|
||||||
#include "InetAddress.hpp"
|
|
||||||
#include "NonCopyable.hpp"
|
|
||||||
#include "MAC.hpp"
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
#include "InetAddress.hpp"
|
||||||
|
#include "MAC.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
|
#include "Thread.hpp"
|
||||||
|
#include "Buffer.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
@ -52,21 +51,35 @@ class RuntimeEnvironment;
|
|||||||
/**
|
/**
|
||||||
* System ethernet tap device
|
* System ethernet tap device
|
||||||
*/
|
*/
|
||||||
class EthernetTap : NonCopyable
|
class EthernetTap : protected Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Construct a new TAP device
|
* Construct a new TAP device
|
||||||
*
|
*
|
||||||
|
* Handler arguments: arg,from,to,etherType,data
|
||||||
|
*
|
||||||
* @param renv Runtime environment
|
* @param renv Runtime environment
|
||||||
* @param mac MAC address of device
|
* @param mac MAC address of device
|
||||||
* @param mtu MTU of device
|
* @param mtu MTU of device
|
||||||
|
* @param handler Handler function to be called when data is received from the tap
|
||||||
|
* @param arg First argument to handler function
|
||||||
* @throws std::runtime_error Unable to allocate device
|
* @throws std::runtime_error Unable to allocate device
|
||||||
*/
|
*/
|
||||||
EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
|
EthernetTap(
|
||||||
|
const RuntimeEnvironment *renv,
|
||||||
|
const MAC &mac,
|
||||||
|
unsigned int mtu,
|
||||||
|
void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
|
||||||
|
void *arg)
|
||||||
throw(std::runtime_error);
|
throw(std::runtime_error);
|
||||||
|
|
||||||
~EthernetTap();
|
/**
|
||||||
|
* Close tap and shut down thread
|
||||||
|
*
|
||||||
|
* This may block for a few seconds while thread exits.
|
||||||
|
*/
|
||||||
|
virtual ~EthernetTap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform OS dependent actions on network configuration change detection
|
* Perform OS dependent actions on network configuration change detection
|
||||||
@ -137,31 +150,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next packet from the interface, blocking if none is available.
|
|
||||||
*
|
|
||||||
* @param from Filled with MAC address of source (normally our own)
|
|
||||||
* @param to Filled with MAC address of destination
|
|
||||||
* @param etherType Filled with Ethernet frame type
|
|
||||||
* @param buf Buffer to fill (must have room for MTU bytes)
|
|
||||||
* @return Number of bytes read or 0 if none
|
|
||||||
*/
|
|
||||||
unsigned int get(MAC &from,MAC &to,unsigned int ðerType,void *buf);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return OS-specific device or connection name
|
* @return OS-specific device or connection name
|
||||||
*/
|
*/
|
||||||
std::string deviceName();
|
std::string deviceName() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if tap is open
|
|
||||||
*/
|
|
||||||
bool open() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close this tap, invalidating the object and causing get() to abort
|
|
||||||
*/
|
|
||||||
void close();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill or modify a set to contain multicast groups for this device
|
* Fill or modify a set to contain multicast groups for this device
|
||||||
@ -177,6 +169,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool updateMulticastGroups(std::set<MulticastGroup> &groups);
|
bool updateMulticastGroups(std::set<MulticastGroup> &groups);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void main()
|
||||||
|
throw();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const MAC _mac;
|
const MAC _mac;
|
||||||
const unsigned int _mtu;
|
const unsigned int _mtu;
|
||||||
@ -186,20 +182,14 @@ private:
|
|||||||
std::set<InetAddress> _ips;
|
std::set<InetAddress> _ips;
|
||||||
Mutex _ips_m;
|
Mutex _ips_m;
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &);
|
||||||
|
void *_arg;
|
||||||
|
|
||||||
|
#ifdef __UNIX_LIKE__
|
||||||
char _dev[16];
|
char _dev[16];
|
||||||
unsigned char *_putBuf;
|
|
||||||
unsigned char *_getBuf;
|
|
||||||
int _fd;
|
int _fd;
|
||||||
|
int _shutdownSignalPipe[2];
|
||||||
bool _isReading;
|
#endif
|
||||||
pthread_t _isReadingThreadId;
|
|
||||||
Mutex _isReading_m;
|
|
||||||
|
|
||||||
#elif defined(_WIN32) /* -------------------------------------------------- */
|
|
||||||
|
|
||||||
#endif /* ----------------------------------------------------------------- */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
@ -32,49 +32,29 @@ namespace ZeroTier {
|
|||||||
|
|
||||||
Network::Network(const RuntimeEnvironment *renv,uint64_t id)
|
Network::Network(const RuntimeEnvironment *renv,uint64_t id)
|
||||||
throw(std::runtime_error) :
|
throw(std::runtime_error) :
|
||||||
Thread(),
|
|
||||||
_r(renv),
|
_r(renv),
|
||||||
_id(id),
|
_id(id),
|
||||||
_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU),
|
_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
|
||||||
_members(),
|
_members(),
|
||||||
_open(false),
|
_open(false),
|
||||||
_lock()
|
_lock()
|
||||||
{
|
{
|
||||||
TRACE("new network %llu created, TAP device: %s",id,_tap.deviceName().c_str());
|
|
||||||
start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::~Network()
|
Network::~Network()
|
||||||
{
|
{
|
||||||
_tap.close();
|
|
||||||
join();
|
|
||||||
TRACE("network %llu (%s) closed",_id,_tap.deviceName().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::main()
|
void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
Buffer<4096> buf;
|
const RuntimeEnvironment *_r = ((Network *)arg)->_r;
|
||||||
MAC from,to;
|
try {
|
||||||
unsigned int etherType = 0;
|
_r->sw->onLocalEthernet(SharedPtr<Network>((Network *)arg),from,to,etherType,data);
|
||||||
|
} catch (std::exception &exc) {
|
||||||
while (_tap.open()) {
|
TRACE("unexpected exception handling local packet: %s",exc.what());
|
||||||
unsigned int len = _tap.get(from,to,etherType,buf.data());
|
} catch ( ... ) {
|
||||||
if (len) {
|
TRACE("unexpected exception handling local packet");
|
||||||
buf.setSize(len);
|
|
||||||
try {
|
|
||||||
if (!*__refCount)
|
|
||||||
break; // sanity check
|
|
||||||
_r->sw->onLocalEthernet(SharedPtr<Network>(this),from,to,etherType,buf);
|
|
||||||
} catch (std::exception &exc) {
|
|
||||||
TRACE("unexpected exception handling local packet: %s",exc.what());
|
|
||||||
} catch ( ... ) {
|
|
||||||
TRACE("unexpected exception handling local packet");
|
|
||||||
}
|
|
||||||
} else break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("network %llu thread terminating",_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
@ -40,8 +40,8 @@
|
|||||||
#include "SharedPtr.hpp"
|
#include "SharedPtr.hpp"
|
||||||
#include "AtomicCounter.hpp"
|
#include "AtomicCounter.hpp"
|
||||||
#include "RuntimeEnvironment.hpp"
|
#include "RuntimeEnvironment.hpp"
|
||||||
#include "Thread.hpp"
|
|
||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
|
#include "NonCopyable.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
@ -50,17 +50,17 @@ class NodeConfig;
|
|||||||
/**
|
/**
|
||||||
* Local network endpoint
|
* Local network endpoint
|
||||||
*/
|
*/
|
||||||
class Network : protected Thread
|
class Network : NonCopyable
|
||||||
{
|
{
|
||||||
friend class SharedPtr<Network>;
|
friend class SharedPtr<Network>;
|
||||||
friend class NodeConfig;
|
friend class NodeConfig;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~Network();
|
|
||||||
|
|
||||||
Network(const RuntimeEnvironment *renv,uint64_t id)
|
Network(const RuntimeEnvironment *renv,uint64_t id)
|
||||||
throw(std::runtime_error);
|
throw(std::runtime_error);
|
||||||
|
|
||||||
|
~Network();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @return Network ID
|
* @return Network ID
|
||||||
@ -141,11 +141,9 @@ public:
|
|||||||
return _multicastGroups;
|
return _multicastGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void main()
|
|
||||||
throw();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
|
||||||
|
|
||||||
const RuntimeEnvironment *_r;
|
const RuntimeEnvironment *_r;
|
||||||
uint64_t _id;
|
uint64_t _id;
|
||||||
EthernetTap _tap;
|
EthernetTap _tap;
|
||||||
|
168
node/Packet.hpp
168
node/Packet.hpp
@ -263,13 +263,13 @@ public:
|
|||||||
setSize(fragLen + ZT_PROTO_MIN_FRAGMENT_LENGTH);
|
setSize(fragLen + ZT_PROTO_MIN_FRAGMENT_LENGTH);
|
||||||
|
|
||||||
// NOTE: this copies both the IV/packet ID and the destination address.
|
// NOTE: this copies both the IV/packet ID and the destination address.
|
||||||
memcpy(_b + ZT_PACKET_FRAGMENT_IDX_PACKET_ID,p.data() + ZT_PACKET_IDX_IV,13);
|
memcpy(field(ZT_PACKET_FRAGMENT_IDX_PACKET_ID,13),p.data() + ZT_PACKET_IDX_IV,13);
|
||||||
|
|
||||||
_b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] = ZT_PACKET_FRAGMENT_INDICATOR;
|
(*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] = ZT_PACKET_FRAGMENT_INDICATOR;
|
||||||
_b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] = (char)(((fragTotal & 0xf) << 4) | (fragNo & 0xf));
|
(*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] = (char)(((fragTotal & 0xf) << 4) | (fragNo & 0xf));
|
||||||
_b[ZT_PACKET_FRAGMENT_IDX_HOPS] = 0;
|
(*this)[ZT_PACKET_FRAGMENT_IDX_HOPS] = 0;
|
||||||
|
|
||||||
memcpy(_b + ZT_PACKET_FRAGMENT_IDX_PAYLOAD,p.data() + fragStart,fragLen);
|
memcpy(field(ZT_PACKET_FRAGMENT_IDX_PAYLOAD,fragLen),p.data() + fragStart,fragLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -277,12 +277,12 @@ public:
|
|||||||
*
|
*
|
||||||
* @return Destination ZT address
|
* @return Destination ZT address
|
||||||
*/
|
*/
|
||||||
inline Address destination() const { return Address(_b + ZT_PACKET_FRAGMENT_IDX_DEST); }
|
inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if fragment is of a valid length
|
* @return True if fragment is of a valid length
|
||||||
*/
|
*/
|
||||||
inline bool lengthValid() const { return (_l >= ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
|
inline bool lengthValid() const { return (size() >= ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ID of packet this is a fragment of
|
* @return ID of packet this is a fragment of
|
||||||
@ -292,36 +292,38 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @return Total number of fragments in packet
|
* @return Total number of fragments in packet
|
||||||
*/
|
*/
|
||||||
inline unsigned int totalFragments() const { return (((unsigned int)_b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] >> 4) & 0xf); }
|
inline unsigned int totalFragments() const { return (((unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO]) >> 4) & 0xf); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Fragment number of this fragment
|
* @return Fragment number of this fragment
|
||||||
*/
|
*/
|
||||||
inline unsigned int fragmentNumber() const { return ((unsigned int)_b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] & 0xf); }
|
inline unsigned int fragmentNumber() const { return ((unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO]) & 0xf); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Fragment ZT hop count
|
* @return Fragment ZT hop count
|
||||||
*/
|
*/
|
||||||
inline unsigned int hops() const { return (unsigned int)_b[ZT_PACKET_FRAGMENT_IDX_HOPS]; }
|
inline unsigned int hops() const { return (unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment this packet's hop count
|
* Increment this packet's hop count
|
||||||
*/
|
*/
|
||||||
inline void incrementHops()
|
inline void incrementHops()
|
||||||
{
|
{
|
||||||
_b[ZT_PACKET_FRAGMENT_IDX_HOPS] = (_b[ZT_PACKET_FRAGMENT_IDX_HOPS] + 1) & ZT_PROTO_MAX_HOPS;
|
(*this)[ZT_PACKET_FRAGMENT_IDX_HOPS] = (((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]) + 1) & ZT_PROTO_MAX_HOPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Fragment payload
|
|
||||||
*/
|
|
||||||
inline unsigned char *payload() { return (unsigned char *)(_b + ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
|
|
||||||
inline const unsigned char *payload() const { return (const unsigned char *)(_b + ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Length of payload in bytes
|
* @return Length of payload in bytes
|
||||||
*/
|
*/
|
||||||
inline unsigned int payloadLength() const { return ((_l > ZT_PACKET_FRAGMENT_IDX_PAYLOAD) ? (_l - ZT_PACKET_FRAGMENT_IDX_PAYLOAD) : 0); }
|
inline unsigned int payloadLength() const { return ((size() > ZT_PACKET_FRAGMENT_IDX_PAYLOAD) ? (size() - ZT_PACKET_FRAGMENT_IDX_PAYLOAD) : 0); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Raw packet payload
|
||||||
|
*/
|
||||||
|
inline const unsigned char *payload() const
|
||||||
|
{
|
||||||
|
return field(ZT_PACKET_FRAGMENT_IDX_PAYLOAD,size() - ZT_PACKET_FRAGMENT_IDX_PAYLOAD);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -495,8 +497,8 @@ public:
|
|||||||
Packet() :
|
Packet() :
|
||||||
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
|
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
|
||||||
{
|
{
|
||||||
Utils::getSecureRandom(_b + ZT_PACKET_IDX_IV,8);
|
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
|
||||||
_b[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
|
(*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -509,10 +511,10 @@ public:
|
|||||||
Packet(const Address &dest,const Address &source,const Verb v) :
|
Packet(const Address &dest,const Address &source,const Verb v) :
|
||||||
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
|
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
|
||||||
{
|
{
|
||||||
Utils::getSecureRandom(_b + ZT_PACKET_IDX_IV,8);
|
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
|
||||||
setDestination(dest);
|
setDestination(dest);
|
||||||
setSource(source);
|
setSource(source);
|
||||||
_b[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
|
(*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
|
||||||
setVerb(v);
|
setVerb(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,10 +528,10 @@ public:
|
|||||||
inline void reset(const Address &dest,const Address &source,const Verb v)
|
inline void reset(const Address &dest,const Address &source,const Verb v)
|
||||||
{
|
{
|
||||||
setSize(ZT_PROTO_MIN_PACKET_LENGTH);
|
setSize(ZT_PROTO_MIN_PACKET_LENGTH);
|
||||||
Utils::getSecureRandom(_b + ZT_PACKET_IDX_IV,8);
|
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
|
||||||
setDestination(dest);
|
setDestination(dest);
|
||||||
setSource(source);
|
setSource(source);
|
||||||
_b[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
|
(*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
|
||||||
setVerb(v);
|
setVerb(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,8 +542,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline void setDestination(const Address &dest)
|
inline void setDestination(const Address &dest)
|
||||||
{
|
{
|
||||||
|
unsigned char *d = field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH);
|
||||||
for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
|
for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
|
||||||
_b[i + ZT_PACKET_IDX_DEST] = dest[i];
|
d[i] = dest[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -551,8 +554,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline void setSource(const Address &source)
|
inline void setSource(const Address &source)
|
||||||
{
|
{
|
||||||
|
unsigned char *s = field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH);
|
||||||
for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
|
for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
|
||||||
_b[i + ZT_PACKET_IDX_SOURCE] = source[i];
|
s[i] = source[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -560,29 +564,29 @@ public:
|
|||||||
*
|
*
|
||||||
* @return Destination ZT address
|
* @return Destination ZT address
|
||||||
*/
|
*/
|
||||||
inline Address destination() const { return Address(_b + ZT_PACKET_IDX_DEST); }
|
inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this packet's source
|
* Get this packet's source
|
||||||
*
|
*
|
||||||
* @return Source ZT address
|
* @return Source ZT address
|
||||||
*/
|
*/
|
||||||
inline Address source() const { return Address(_b + ZT_PACKET_IDX_SOURCE); }
|
inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if packet is of valid length
|
* @return True if packet is of valid length
|
||||||
*/
|
*/
|
||||||
inline bool lengthValid() const { return (_l >= ZT_PROTO_MIN_PACKET_LENGTH); }
|
inline bool lengthValid() const { return (size() >= ZT_PROTO_MIN_PACKET_LENGTH); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if packet is encrypted
|
* @return True if packet is encrypted
|
||||||
*/
|
*/
|
||||||
inline bool encrypted() const { return (((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_ENCRYPTED)); }
|
inline bool encrypted() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_ENCRYPTED)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if packet is fragmented (expect fragments)
|
* @return True if packet is fragmented (expect fragments)
|
||||||
*/
|
*/
|
||||||
inline bool fragmented() const { return (((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED)); }
|
inline bool fragmented() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this packet's fragmented flag
|
* Set this packet's fragmented flag
|
||||||
@ -592,26 +596,26 @@ public:
|
|||||||
inline void setFragmented(bool f)
|
inline void setFragmented(bool f)
|
||||||
{
|
{
|
||||||
if (f)
|
if (f)
|
||||||
_b[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_FRAGMENTED;
|
(*this)[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_FRAGMENTED;
|
||||||
else _b[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_FRAGMENTED);
|
else (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_FRAGMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if compressed (result only valid if unencrypted)
|
* @return True if compressed (result only valid if unencrypted)
|
||||||
*/
|
*/
|
||||||
inline bool compressed() const { return (((unsigned char)_b[ZT_PACKET_IDX_VERB] & ZT_PROTO_VERB_FLAG_COMPRESSED)); }
|
inline bool compressed() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_VERB] & ZT_PROTO_VERB_FLAG_COMPRESSED)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ZeroTier forwarding hops (0 to 7)
|
* @return ZeroTier forwarding hops (0 to 7)
|
||||||
*/
|
*/
|
||||||
inline unsigned int hops() const { return ((unsigned int)_b[ZT_PACKET_IDX_FLAGS] & 0x07); }
|
inline unsigned int hops() const { return ((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x07); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment this packet's hop count
|
* Increment this packet's hop count
|
||||||
*/
|
*/
|
||||||
inline void incrementHops()
|
inline void incrementHops()
|
||||||
{
|
{
|
||||||
_b[ZT_PACKET_IDX_FLAGS] = (char)((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & 0xf8) | (((unsigned char)_b[ZT_PACKET_IDX_FLAGS] + 1) & 0x07);
|
(*this)[ZT_PACKET_IDX_FLAGS] = (char)((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & 0xf8) | (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] + 1) & 0x07);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -629,23 +633,25 @@ public:
|
|||||||
*
|
*
|
||||||
* @param v New packet verb
|
* @param v New packet verb
|
||||||
*/
|
*/
|
||||||
inline void setVerb(Verb v) { _b[ZT_PACKET_IDX_VERB] = (char)v; }
|
inline void setVerb(Verb v) { (*this)[ZT_PACKET_IDX_VERB] = (char)v; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Packet verb (not including flag bits)
|
* @return Packet verb (not including flag bits)
|
||||||
*/
|
*/
|
||||||
inline Verb verb() const { return (Verb)(_b[ZT_PACKET_IDX_VERB] & 0x1f); }
|
inline Verb verb() const { return (Verb)((*this)[ZT_PACKET_IDX_VERB] & 0x1f); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Length of packet payload
|
* @return Length of packet payload
|
||||||
*/
|
*/
|
||||||
inline unsigned int payloadLength() const throw() { return ((_l < ZT_PROTO_MIN_PACKET_LENGTH) ? 0 : (_l - ZT_PROTO_MIN_PACKET_LENGTH)); }
|
inline unsigned int payloadLength() const { return ((size() < ZT_PROTO_MIN_PACKET_LENGTH) ? 0 : (size() - ZT_PROTO_MIN_PACKET_LENGTH)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Packet payload
|
* @return Raw packet payload
|
||||||
*/
|
*/
|
||||||
inline unsigned char *payload() throw() { return (unsigned char *)(_b + ZT_PACKET_IDX_PAYLOAD); }
|
inline const unsigned char *payload() const
|
||||||
inline const unsigned char *payload() const throw() { return (const unsigned char *)(_b + ZT_PACKET_IDX_PAYLOAD); }
|
{
|
||||||
|
return field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the HMAC of this packet's payload and set HMAC field
|
* Compute the HMAC of this packet's payload and set HMAC field
|
||||||
@ -655,13 +661,13 @@ public:
|
|||||||
* @param key 256-bit (32 byte) key
|
* @param key 256-bit (32 byte) key
|
||||||
*/
|
*/
|
||||||
inline void hmacSet(const void *key)
|
inline void hmacSet(const void *key)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
unsigned char mac[32];
|
unsigned char mac[32];
|
||||||
unsigned char key2[32];
|
unsigned char key2[32];
|
||||||
_mangleKey((const unsigned char *)key,key2);
|
_mangleKey((const unsigned char *)key,key2);
|
||||||
HMAC::sha256(key2,sizeof(key2),_b + ZT_PACKET_IDX_VERB,(_l >= ZT_PACKET_IDX_VERB) ? (_l - ZT_PACKET_IDX_VERB) : 0,mac);
|
unsigned int hmacLen = (size() >= ZT_PACKET_IDX_VERB) ? (size() - ZT_PACKET_IDX_VERB) : 0;
|
||||||
memcpy(_b + ZT_PACKET_IDX_HMAC,mac,8);
|
HMAC::sha256(key2,sizeof(key2),field(ZT_PACKET_IDX_VERB,hmacLen),hmacLen,mac);
|
||||||
|
memcpy(field(ZT_PACKET_IDX_HMAC,8),mac,8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -672,15 +678,15 @@ public:
|
|||||||
* @param key 256-bit (32 byte) key
|
* @param key 256-bit (32 byte) key
|
||||||
*/
|
*/
|
||||||
inline bool hmacVerify(const void *key) const
|
inline bool hmacVerify(const void *key) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
unsigned char mac[32];
|
unsigned char mac[32];
|
||||||
unsigned char key2[32];
|
unsigned char key2[32];
|
||||||
if (_l < ZT_PACKET_IDX_VERB)
|
if (size() < ZT_PACKET_IDX_VERB)
|
||||||
return false; // incomplete packets fail
|
return false; // incomplete packets fail
|
||||||
_mangleKey((const unsigned char *)key,key2);
|
_mangleKey((const unsigned char *)key,key2);
|
||||||
HMAC::sha256(key2,sizeof(key2),_b + ZT_PACKET_IDX_VERB,_l - ZT_PACKET_IDX_VERB,mac);
|
unsigned int hmacLen = size() - ZT_PACKET_IDX_VERB;
|
||||||
return (!memcmp(_b + ZT_PACKET_IDX_HMAC,mac,8));
|
HMAC::sha256(key2,sizeof(key2),field(ZT_PACKET_IDX_VERB,hmacLen),hmacLen,mac);
|
||||||
|
return (!memcmp(field(ZT_PACKET_IDX_HMAC,8),mac,8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -689,13 +695,16 @@ public:
|
|||||||
* @param key 256-bit (32 byte) key
|
* @param key 256-bit (32 byte) key
|
||||||
*/
|
*/
|
||||||
inline void encrypt(const void *key)
|
inline void encrypt(const void *key)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
_b[ZT_PACKET_IDX_FLAGS] |= ZT_PROTO_FLAG_ENCRYPTED;
|
(*this)[ZT_PACKET_IDX_FLAGS] |= ZT_PROTO_FLAG_ENCRYPTED;
|
||||||
unsigned char key2[32];
|
unsigned char key2[32];
|
||||||
_mangleKey((const unsigned char *)key,key2);
|
if (size() >= ZT_PACKET_IDX_VERB) {
|
||||||
Salsa20 s20(key2,256,_b + ZT_PACKET_IDX_IV);
|
_mangleKey((const unsigned char *)key,key2);
|
||||||
s20.encrypt(_b + ZT_PACKET_IDX_VERB,_b + ZT_PACKET_IDX_VERB,(_l >= ZT_PACKET_IDX_VERB) ? (_l - ZT_PACKET_IDX_VERB) : 0);
|
Salsa20 s20(key2,256,field(ZT_PACKET_IDX_IV,8));
|
||||||
|
unsigned int encLen = size() - ZT_PACKET_IDX_VERB;
|
||||||
|
unsigned char *const encBuf = field(ZT_PACKET_IDX_VERB,encLen);
|
||||||
|
s20.encrypt(encBuf,encBuf,encLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -704,13 +713,16 @@ public:
|
|||||||
* @param key 256-bit (32 byte) key
|
* @param key 256-bit (32 byte) key
|
||||||
*/
|
*/
|
||||||
inline void decrypt(const void *key)
|
inline void decrypt(const void *key)
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
unsigned char key2[32];
|
unsigned char key2[32];
|
||||||
_mangleKey((const unsigned char *)key,key2);
|
if (size() >= ZT_PACKET_IDX_VERB) {
|
||||||
Salsa20 s20(key2,256,_b + ZT_PACKET_IDX_IV);
|
_mangleKey((const unsigned char *)key,key2);
|
||||||
s20.decrypt(_b + ZT_PACKET_IDX_VERB,_b + ZT_PACKET_IDX_VERB,(_l >= ZT_PACKET_IDX_VERB) ? (_l - ZT_PACKET_IDX_VERB) : 0);
|
Salsa20 s20(key2,256,field(ZT_PACKET_IDX_IV,8));
|
||||||
_b[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED);
|
unsigned int decLen = size() - ZT_PACKET_IDX_VERB;
|
||||||
|
unsigned char *const decBuf = field(ZT_PACKET_IDX_VERB,decLen);
|
||||||
|
s20.decrypt(decBuf,decBuf,decLen);
|
||||||
|
}
|
||||||
|
(*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -724,20 +736,19 @@ public:
|
|||||||
* @return True if compression occurred
|
* @return True if compression occurred
|
||||||
*/
|
*/
|
||||||
inline bool compress()
|
inline bool compress()
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
|
unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
|
||||||
if ((!compressed())&&(_l > (ZT_PACKET_IDX_PAYLOAD + 32))) {
|
if ((!compressed())&&(size() > (ZT_PACKET_IDX_PAYLOAD + 32))) {
|
||||||
int pl = (int)(_l - ZT_PACKET_IDX_PAYLOAD);
|
int pl = (int)(size() - ZT_PACKET_IDX_PAYLOAD);
|
||||||
int cl = LZ4_compress((const char *)(_b + ZT_PACKET_IDX_PAYLOAD),(char *)buf,pl);
|
int cl = LZ4_compress((const char *)field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)pl),(char *)buf,pl);
|
||||||
if ((cl > 0)&&(cl < pl)) {
|
if ((cl > 0)&&(cl < pl)) {
|
||||||
_b[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
|
(*this)[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
|
||||||
memcpy(_b + ZT_PACKET_IDX_PAYLOAD,buf,cl);
|
setSize((unsigned int)cl + ZT_PACKET_IDX_PAYLOAD);
|
||||||
_l = (unsigned int)cl + ZT_PACKET_IDX_PAYLOAD;
|
memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)cl),buf,cl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_b[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
|
(*this)[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,18 +761,18 @@ public:
|
|||||||
* @return True if data is now decompressed and valid, false on error
|
* @return True if data is now decompressed and valid, false on error
|
||||||
*/
|
*/
|
||||||
inline bool uncompress()
|
inline bool uncompress()
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH];
|
unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH];
|
||||||
if ((compressed())&&(_l >= ZT_PROTO_MIN_PACKET_LENGTH)) {
|
if ((compressed())&&(size() >= ZT_PROTO_MIN_PACKET_LENGTH)) {
|
||||||
if (_l > ZT_PACKET_IDX_PAYLOAD) {
|
if (size() > ZT_PACKET_IDX_PAYLOAD) {
|
||||||
int ucl = LZ4_uncompress_unknownOutputSize((const char *)(_b + ZT_PACKET_IDX_PAYLOAD),(char *)buf,_l - ZT_PACKET_IDX_PAYLOAD,sizeof(buf));
|
unsigned int compLen = size() - ZT_PACKET_IDX_PAYLOAD;
|
||||||
|
int ucl = LZ4_uncompress_unknownOutputSize((const char *)field(ZT_PACKET_IDX_PAYLOAD,compLen),(char *)buf,compLen,sizeof(buf));
|
||||||
if ((ucl > 0)&&(ucl <= (int)(capacity() - ZT_PACKET_IDX_PAYLOAD))) {
|
if ((ucl > 0)&&(ucl <= (int)(capacity() - ZT_PACKET_IDX_PAYLOAD))) {
|
||||||
memcpy(_b + ZT_PACKET_IDX_PAYLOAD,buf,ucl);
|
setSize((unsigned int)ucl + ZT_PACKET_IDX_PAYLOAD);
|
||||||
_l = (unsigned int)ucl + ZT_PACKET_IDX_PAYLOAD;
|
memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)ucl),buf,ucl);
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
_b[ZT_PACKET_IDX_VERB] &= ~ZT_PROTO_VERB_FLAG_COMPRESSED;
|
(*this)[ZT_PACKET_IDX_VERB] &= ~ZT_PROTO_VERB_FLAG_COMPRESSED;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -788,19 +799,18 @@ private:
|
|||||||
* @param out Output buffer (32 bytes)
|
* @param out Output buffer (32 bytes)
|
||||||
*/
|
*/
|
||||||
inline void _mangleKey(const unsigned char *in,unsigned char *out) const
|
inline void _mangleKey(const unsigned char *in,unsigned char *out) const
|
||||||
throw()
|
|
||||||
{
|
{
|
||||||
// Random IV (Salsa20 also uses the IV natively, but HMAC doesn't), and
|
// Random IV (Salsa20 also uses the IV natively, but HMAC doesn't), and
|
||||||
// destination and source addresses. Using dest and source addresses
|
// destination and source addresses. Using dest and source addresses
|
||||||
// gives us a (likely) different key space for a->b vs b->a.
|
// gives us a (likely) different key space for a->b vs b->a.
|
||||||
for(unsigned int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18
|
for(unsigned int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18
|
||||||
out[i] = in[i] ^ (unsigned char)_b[i];
|
out[i] = in[i] ^ (unsigned char)(*this)[i];
|
||||||
// Flags, but masking off hop count which is altered by forwarding nodes
|
// Flags, but masking off hop count which is altered by forwarding nodes
|
||||||
out[18] = in[18] ^ ((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & 0xf8);
|
out[18] = in[18] ^ ((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & 0xf8);
|
||||||
// Raw packet size in bytes -- each raw packet size defines a possibly
|
// Raw packet size in bytes -- each raw packet size defines a possibly
|
||||||
// different space of keys.
|
// different space of keys.
|
||||||
out[19] = in[19] ^ (unsigned char)(_l & 0xff);
|
out[19] = in[19] ^ (unsigned char)(size() & 0xff);
|
||||||
out[20] = in[20] ^ (unsigned char)((_l >> 8) & 0xff); // little endian
|
out[20] = in[20] ^ (unsigned char)((size() >> 8) & 0xff); // little endian
|
||||||
// Rest of raw key is used unchanged
|
// Rest of raw key is used unchanged
|
||||||
for(unsigned int i=21;i<32;++i)
|
for(unsigned int i=21;i<32;++i)
|
||||||
out[i] = in[i];
|
out[i] = in[i];
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Salsa20.hpp"
|
#include "Salsa20.hpp"
|
||||||
|
#include "Constants.hpp"
|
||||||
|
|
||||||
#define ROTATE(v,c) (((v) << (c)) | ((v) >> (32 - (c))))
|
#define ROTATE(v,c) (((v) << (c)) | ((v) >> (32 - (c))))
|
||||||
#define XOR(v,w) ((v) ^ (w))
|
#define XOR(v,w) ((v) ^ (w))
|
||||||
|
@ -291,6 +291,8 @@ static int testNet()
|
|||||||
std::cout << "[net] GET http://www.google.com/" << std::endl;
|
std::cout << "[net] GET http://www.google.com/" << std::endl;
|
||||||
new Http::Request(Http::HTTP_METHOD_GET,"http://www.google.com/",Http::EMPTY_HEADERS,std::string(),&testHttpHandler,(void *)0);
|
new Http::Request(Http::HTTP_METHOD_GET,"http://www.google.com/",Http::EMPTY_HEADERS,std::string(),&testHttpHandler,(void *)0);
|
||||||
testHttpDoneCondition.wait();
|
testHttpDoneCondition.wait();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc,char **argv)
|
int main(int argc,char **argv)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user