OSX no longer requires the kext due to feth black magic! The MacEthernetTapAgent must be installed in /Library/Application Support/ZeroTier/One for ZT to work now. Eventually this can let us do an app bundle, get rid of the pkg, and have ZT itself run with normal or reduced privileges. Also fixes GitHub issue #870 (at least for me) and may be faster than the old kext.
This commit is contained in:
parent
7c72653385
commit
2e44b90f63
7 changed files with 957 additions and 10 deletions
404
osdep/MacEthernetTap.cpp
Normal file
404
osdep/MacEthernetTap.cpp
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2018 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/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* You can be released from the requirements of the license by purchasing
|
||||
* a commercial license. Buying such a license is mandatory as soon as you
|
||||
* develop commercial closed-source software that incorporates or links
|
||||
* directly against ZeroTier software without disclosing the source code
|
||||
* of your own application.
|
||||
*/
|
||||
|
||||
#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_dl.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#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 "MacEthernetTap.hpp"
|
||||
#include "MacEthernetTapAgent.h"
|
||||
|
||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static Mutex globalTapCreateLock;
|
||||
|
||||
MacEthernetTap::MacEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
|
||||
void *arg) :
|
||||
_handler(handler),
|
||||
_arg(arg),
|
||||
_nwid(nwid),
|
||||
_homePath(homePath),
|
||||
_mtu(mtu),
|
||||
_metric(metric),
|
||||
_agentStdin(-1),
|
||||
_agentStdout(-1),
|
||||
_agentStderr(-1),
|
||||
_agentStdin2(-1),
|
||||
_agentStdout2(-1),
|
||||
_agentStderr2(-1),
|
||||
_agentPid(-1),
|
||||
_enabled(true)
|
||||
{
|
||||
char ethaddr[64],mtustr[16],devnostr[16],devstr[16],metricstr[16];
|
||||
OSUtils::ztsnprintf(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]);
|
||||
OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",mtu);
|
||||
OSUtils::ztsnprintf(metricstr,sizeof(metricstr),"%u",metric);
|
||||
|
||||
struct ifaddrs *ifa = (struct ifaddrs *)0;
|
||||
std::set<std::string> ifns;
|
||||
if (!getifaddrs(&ifa)) {
|
||||
struct ifaddrs *p = ifa;
|
||||
while (p) {
|
||||
ifns.insert(std::string(p->ifa_name));
|
||||
p = p->ifa_next;
|
||||
}
|
||||
freeifaddrs(ifa);
|
||||
}
|
||||
|
||||
Mutex::Lock _gl(globalTapCreateLock); // only make one at a time
|
||||
|
||||
unsigned int devNo = (nwid ^ (nwid >> 32) ^ (nwid >> 48)) % 5000;
|
||||
for(int tries=0;tries<16;++tries) {
|
||||
OSUtils::ztsnprintf(devstr,sizeof(devstr),"feth%u",devNo);
|
||||
_dev = devstr;
|
||||
if (!ifns.count(_dev))
|
||||
break;
|
||||
devNo = (devNo + 1) % 5000;
|
||||
}
|
||||
OSUtils::ztsnprintf(devnostr,sizeof(devnostr),"%u",devNo);
|
||||
|
||||
if (::pipe(_shutdownSignalPipe)) {
|
||||
throw std::runtime_error("pipe creation failed");
|
||||
}
|
||||
|
||||
int agentStdin[2];
|
||||
int agentStdout[2];
|
||||
int agentStderr[2];
|
||||
if (::pipe(agentStdin)) {
|
||||
throw std::runtime_error("pipe creation failed");
|
||||
}
|
||||
if (::pipe(agentStdout)) {
|
||||
throw std::runtime_error("pipe creation failed");
|
||||
}
|
||||
if (::pipe(agentStderr)) {
|
||||
throw std::runtime_error("pipe creation failed");
|
||||
}
|
||||
_agentStdin = agentStdin[1];
|
||||
_agentStdout = agentStdout[0];
|
||||
_agentStderr = agentStderr[0];
|
||||
_agentStdin2 = agentStdin[0];
|
||||
_agentStdout2 = agentStdout[1];
|
||||
_agentStderr2 = agentStderr[1];
|
||||
long apid = (long)vfork();
|
||||
if (apid < 0) {
|
||||
throw std::runtime_error("fork failed");
|
||||
} else if (apid == 0) {
|
||||
::dup2(agentStdin[0],STDIN_FILENO);
|
||||
::dup2(agentStdout[1],STDOUT_FILENO);
|
||||
::dup2(agentStderr[1],STDERR_FILENO);
|
||||
::close(agentStdin[0]);
|
||||
::close(agentStdout[1]);
|
||||
::close(agentStderr[1]);
|
||||
::execl(ZT_MACETHERNETTAPAGENT_DEFAULT_SYSTEM_PATH,ZT_MACETHERNETTAPAGENT_DEFAULT_SYSTEM_PATH,devnostr,ethaddr,mtustr,metricstr,(char *)0);
|
||||
::exit(-1);
|
||||
} else {
|
||||
_agentPid = apid;
|
||||
}
|
||||
Thread::sleep(100); // this causes them to come up in a more user-friendly order on launch
|
||||
|
||||
_thread = Thread::start(this);
|
||||
}
|
||||
|
||||
MacEthernetTap::~MacEthernetTap()
|
||||
{
|
||||
Mutex::Lock _gl(globalTapCreateLock);
|
||||
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
||||
Thread::join(_thread);
|
||||
::close(_shutdownSignalPipe[0]);
|
||||
::close(_shutdownSignalPipe[1]);
|
||||
int ec = 0;
|
||||
::kill(_agentPid,SIGTERM);
|
||||
::waitpid(_agentPid,&ec,0);
|
||||
::close(_agentStdin);
|
||||
::close(_agentStdout);
|
||||
::close(_agentStderr);
|
||||
::close(_agentStdin2);
|
||||
::close(_agentStdout2);
|
||||
::close(_agentStderr2);
|
||||
}
|
||||
|
||||
void MacEthernetTap::setEnabled(bool en) { _enabled = en; }
|
||||
bool MacEthernetTap::enabled() const { return _enabled; }
|
||||
|
||||
bool MacEthernetTap::addIp(const InetAddress &ip)
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
if (!ip)
|
||||
return false;
|
||||
|
||||
std::string cmd;
|
||||
cmd.push_back((char)ZT_MACETHERNETTAPAGENT_STDIN_CMD_IFCONFIG);
|
||||
cmd.append((ip.ss_family == AF_INET6) ? "inet6" : "inet");
|
||||
cmd.push_back(0);
|
||||
cmd.append(ip.toString(tmp));
|
||||
cmd.push_back(0);
|
||||
cmd.append("alias");
|
||||
cmd.push_back(0);
|
||||
|
||||
uint16_t l = (uint16_t)cmd.length();
|
||||
write(_agentStdin,&l,2);
|
||||
write(_agentStdin,cmd.data(),cmd.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacEthernetTap::removeIp(const InetAddress &ip)
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
if (!ip)
|
||||
return false;
|
||||
|
||||
std::string cmd;
|
||||
cmd.push_back((char)ZT_MACETHERNETTAPAGENT_STDIN_CMD_IFCONFIG);
|
||||
cmd.append((ip.ss_family == AF_INET6) ? "inet6" : "inet");
|
||||
cmd.push_back(0);
|
||||
cmd.append(ip.toString(tmp));
|
||||
cmd.push_back(0);
|
||||
cmd.append("-alias");
|
||||
cmd.push_back(0);
|
||||
|
||||
uint16_t l = (uint16_t)cmd.length();
|
||||
write(_agentStdin,&l,2);
|
||||
write(_agentStdin,cmd.data(),cmd.length());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> MacEthernetTap::ips() const
|
||||
{
|
||||
struct ifaddrs *ifa = (struct ifaddrs *)0;
|
||||
std::vector<InetAddress> r;
|
||||
if (!getifaddrs(&ifa)) {
|
||||
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;
|
||||
}
|
||||
freeifaddrs(ifa);
|
||||
}
|
||||
std::sort(r.begin(),r.end());
|
||||
r.erase(std::unique(r.begin(),r.end()),r.end());
|
||||
return r;
|
||||
}
|
||||
|
||||
void MacEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
||||
{
|
||||
struct iovec iov[3];
|
||||
unsigned char hdr[15];
|
||||
uint16_t l;
|
||||
if ((_agentStdin > 0)&&(len <= _mtu)&&(_enabled)) {
|
||||
hdr[0] = ZT_MACETHERNETTAPAGENT_STDIN_CMD_PACKET;
|
||||
to.copyTo(hdr + 1,6);
|
||||
from.copyTo(hdr + 7,6);
|
||||
hdr[13] = (char)((etherType >> 8) & 0xff);
|
||||
hdr[14] = (char)(etherType & 0xff);
|
||||
l = (uint16_t)(len + 15);
|
||||
iov[0].iov_base = &l;
|
||||
iov[0].iov_len = 2;
|
||||
iov[1].iov_base = hdr;
|
||||
iov[1].iov_len = 15;
|
||||
iov[2].iov_base = const_cast<void *>(data);
|
||||
iov[2].iov_len = len;
|
||||
writev(_agentStdin,iov,3);
|
||||
}
|
||||
}
|
||||
|
||||
std::string MacEthernetTap::deviceName() const { return _dev; }
|
||||
void MacEthernetTap::setFriendlyName(const char *friendlyName) {}
|
||||
|
||||
void MacEthernetTap::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);
|
||||
}
|
||||
|
||||
void MacEthernetTap::setMtu(unsigned int mtu)
|
||||
{
|
||||
char tmp[16];
|
||||
std::string cmd;
|
||||
cmd.push_back((char)ZT_MACETHERNETTAPAGENT_STDIN_CMD_IFCONFIG);
|
||||
cmd.append("mtu");
|
||||
cmd.push_back(0);
|
||||
OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
|
||||
cmd.append(tmp);
|
||||
cmd.push_back(0);
|
||||
uint16_t l = (uint16_t)cmd.length();
|
||||
write(_agentStdin,&l,2);
|
||||
write(_agentStdin,cmd.data(),cmd.length());
|
||||
_mtu = mtu;
|
||||
}
|
||||
|
||||
void MacEthernetTap::threadMain()
|
||||
throw()
|
||||
{
|
||||
char agentReadBuf[262144];
|
||||
fd_set readfds,nullfds;
|
||||
MAC to,from;
|
||||
|
||||
Thread::sleep(250);
|
||||
|
||||
const int nfds = std::max(std::max(_shutdownSignalPipe[0],_agentStdout),_agentStderr) + 1;
|
||||
long agentReadPtr = 0;
|
||||
fcntl(_agentStdout,F_SETFL,O_NONBLOCK);
|
||||
fcntl(_agentStderr,F_SETFL,O_NONBLOCK);
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&nullfds);
|
||||
for(;;) {
|
||||
FD_SET(_shutdownSignalPipe[0],&readfds);
|
||||
FD_SET(_agentStdout,&readfds);
|
||||
FD_SET(_agentStderr,&readfds);
|
||||
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
|
||||
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) {
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(_agentStdout,&readfds)) {
|
||||
long n = (long)read(_agentStdout,agentReadBuf + agentReadPtr,sizeof(agentReadBuf) - agentReadPtr);
|
||||
if (n > 0) {
|
||||
agentReadPtr += n;
|
||||
while (agentReadPtr >= 2) {
|
||||
long len = *((uint16_t *)agentReadBuf);
|
||||
if (agentReadPtr >= (len + 2)) {
|
||||
char *msg = agentReadBuf + 2;
|
||||
|
||||
if ((len > 14)&&(_enabled)) {
|
||||
to.setTo(msg,6);
|
||||
from.setTo(msg + 6,6);
|
||||
_handler(_arg,(void *)0,_nwid,from,to,ntohs(((const uint16_t *)msg)[6]),0,(const void *)(msg + 14),(unsigned int)len - 14);
|
||||
}
|
||||
|
||||
if (agentReadPtr > (len + 2)) {
|
||||
memmove(agentReadBuf,agentReadBuf + len + 2,agentReadPtr -= (len + 2));
|
||||
} else {
|
||||
agentReadPtr = 0;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(_agentStderr,&readfds)) {
|
||||
read(_agentStderr,agentReadBuf,sizeof(agentReadBuf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
Loading…
Add table
Add a link
Reference in a new issue