mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 03:53:44 +02:00
Make EthernetTap creation occur in a background thread in Network since it's a time consuming operation on Windows. This fixes one of the last remaining Windows problems.
This commit is contained in:
parent
e0cb5caef2
commit
f80ec871f6
12 changed files with 239 additions and 105 deletions
|
@ -121,7 +121,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||||
ui->bottomContainerWidget->setVisible(false);
|
ui->bottomContainerWidget->setVisible(false);
|
||||||
ui->networkListWidget->setVisible(false);
|
ui->networkListWidget->setVisible(false);
|
||||||
|
|
||||||
this->pollServiceTimerId = this->startTimer(1000);
|
this->firstTimerTick = true;
|
||||||
|
this->pollServiceTimerId = this->startTimer(200);
|
||||||
this->cyclesSinceResponseFromService = 0;
|
this->cyclesSinceResponseFromService = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,15 +134,19 @@ MainWindow::~MainWindow()
|
||||||
mainWindow = (MainWindow *)0;
|
mainWindow = (MainWindow *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::timerEvent(QTimerEvent *event)
|
void MainWindow::timerEvent(QTimerEvent *event) // event can be null since code also calls this directly
|
||||||
{
|
{
|
||||||
event->accept();
|
|
||||||
|
|
||||||
if (this->isHidden())
|
if (this->isHidden())
|
||||||
return;
|
return;
|
||||||
if (pollServiceTimerId < 0)
|
if (this->pollServiceTimerId < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (this->firstTimerTick) {
|
||||||
|
this->firstTimerTick = false;
|
||||||
|
this->killTimer(this->pollServiceTimerId);
|
||||||
|
this->pollServiceTimerId = this->startTimer(1500);
|
||||||
|
}
|
||||||
|
|
||||||
if (!zeroTierClient) {
|
if (!zeroTierClient) {
|
||||||
std::string authToken;
|
std::string authToken;
|
||||||
if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) {
|
if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) {
|
||||||
|
|
|
@ -86,6 +86,7 @@ private:
|
||||||
QString myAddress;
|
QString myAddress;
|
||||||
QString myStatus;
|
QString myStatus;
|
||||||
QString myVersion;
|
QString myVersion;
|
||||||
|
bool firstTimerTick;
|
||||||
int pollServiceTimerId;
|
int pollServiceTimerId;
|
||||||
unsigned int numPeers;
|
unsigned int numPeers;
|
||||||
unsigned int cyclesSinceResponseFromService;
|
unsigned int cyclesSinceResponseFromService;
|
||||||
|
|
|
@ -64,6 +64,9 @@ QListWidget.ipAddressList::item {
|
||||||
}
|
}
|
||||||
QListWidget.ipAddressList::item:selected {
|
QListWidget.ipAddressList::item:selected {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
border-top: 0;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
}
|
}
|
||||||
QListWidget.ipAddressList::item:hover {
|
QListWidget.ipAddressList::item:hover {
|
||||||
|
|
|
@ -336,8 +336,6 @@ EthernetTap::EthernetTap(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
whack(); // turns on IPv6 on OSX
|
|
||||||
|
|
||||||
::pipe(_shutdownSignalPipe);
|
::pipe(_shutdownSignalPipe);
|
||||||
|
|
||||||
_thread = Thread::start(this);
|
_thread = Thread::start(this);
|
||||||
|
@ -378,7 +376,7 @@ EthernetTap::~EthernetTap()
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
/*
|
||||||
void EthernetTap::whack()
|
void EthernetTap::whack()
|
||||||
{
|
{
|
||||||
const char *ipconfig = UNIX_COMMANDS[ZT_MAC_IPCONFIG_COMMAND];
|
const char *ipconfig = UNIX_COMMANDS[ZT_MAC_IPCONFIG_COMMAND];
|
||||||
|
@ -393,9 +391,7 @@ void EthernetTap::whack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
*/
|
||||||
void EthernetTap::whack() {}
|
|
||||||
#endif // __APPLE__ / !__APPLE__
|
|
||||||
|
|
||||||
void EthernetTap::setDisplayName(const char *dn)
|
void EthernetTap::setDisplayName(const char *dn)
|
||||||
{
|
{
|
||||||
|
@ -979,7 +975,8 @@ EthernetTap::EthernetTap(
|
||||||
_arg(arg),
|
_arg(arg),
|
||||||
_tap(INVALID_HANDLE_VALUE),
|
_tap(INVALID_HANDLE_VALUE),
|
||||||
_injectSemaphore(INVALID_HANDLE_VALUE),
|
_injectSemaphore(INVALID_HANDLE_VALUE),
|
||||||
_run(true)
|
_run(true),
|
||||||
|
_initialized(false)
|
||||||
{
|
{
|
||||||
char subkeyName[4096];
|
char subkeyName[4096];
|
||||||
char subkeyClass[4096];
|
char subkeyClass[4096];
|
||||||
|
@ -1203,11 +1200,15 @@ EthernetTap::EthernetTap(
|
||||||
// Start background thread that actually performs I/O
|
// Start background thread that actually performs I/O
|
||||||
_injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
|
_injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
|
||||||
_thread = Thread::start(this);
|
_thread = Thread::start(this);
|
||||||
|
|
||||||
|
// Certain functions can now work (e.g. ips())
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EthernetTap::~EthernetTap()
|
EthernetTap::~EthernetTap()
|
||||||
{
|
{
|
||||||
_run = false;
|
_run = false;
|
||||||
|
|
||||||
ReleaseSemaphore(_injectSemaphore,1,NULL);
|
ReleaseSemaphore(_injectSemaphore,1,NULL);
|
||||||
Thread::join(_thread);
|
Thread::join(_thread);
|
||||||
CloseHandle(_tap);
|
CloseHandle(_tap);
|
||||||
|
@ -1237,12 +1238,10 @@ EthernetTap::~EthernetTap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EthernetTap::whack()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void EthernetTap::setDisplayName(const char *dn)
|
void EthernetTap::setDisplayName(const char *dn)
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
return;
|
||||||
HKEY ifp;
|
HKEY ifp;
|
||||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _myDeviceInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) {
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _myDeviceInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) {
|
||||||
RegSetKeyValueA(ifp,"Connection","Name",REG_SZ,(LPCVOID)dn,(DWORD)(strlen(dn)+1));
|
RegSetKeyValueA(ifp,"Connection","Name",REG_SZ,(LPCVOID)dn,(DWORD)(strlen(dn)+1));
|
||||||
|
@ -1252,6 +1251,8 @@ void EthernetTap::setDisplayName(const char *dn)
|
||||||
|
|
||||||
bool EthernetTap::addIP(const InetAddress &ip)
|
bool EthernetTap::addIP(const InetAddress &ip)
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
return false;
|
||||||
if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
|
if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1299,6 +1300,8 @@ bool EthernetTap::addIP(const InetAddress &ip)
|
||||||
|
|
||||||
bool EthernetTap::removeIP(const InetAddress &ip)
|
bool EthernetTap::removeIP(const InetAddress &ip)
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
return false;
|
||||||
try {
|
try {
|
||||||
MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
|
MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
|
||||||
std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
|
std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
|
||||||
|
@ -1339,6 +1342,9 @@ std::set<InetAddress> EthernetTap::ips() const
|
||||||
static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
|
static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
|
||||||
std::set<InetAddress> addrs;
|
std::set<InetAddress> addrs;
|
||||||
|
|
||||||
|
if (!_initialized)
|
||||||
|
return addrs;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
|
MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
|
||||||
std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
|
std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
|
||||||
|
@ -1369,6 +1375,8 @@ std::set<InetAddress> EthernetTap::ips() const
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
return;
|
||||||
if (len > (ZT_IF_MTU))
|
if (len > (ZT_IF_MTU))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1398,6 +1406,9 @@ std::string EthernetTap::persistentId() const
|
||||||
|
|
||||||
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
return false;
|
||||||
|
|
||||||
std::set<MulticastGroup> newGroups;
|
std::set<MulticastGroup> newGroups;
|
||||||
|
|
||||||
// Ensure that groups are added for each IP... this handles the MAC:ADI
|
// Ensure that groups are added for each IP... this handles the MAC:ADI
|
||||||
|
@ -1456,7 +1467,7 @@ void EthernetTap::threadMain()
|
||||||
HANDLE wait4[3];
|
HANDLE wait4[3];
|
||||||
wait4[0] = _injectSemaphore;
|
wait4[0] = _injectSemaphore;
|
||||||
wait4[1] = _tapOvlRead.hEvent;
|
wait4[1] = _tapOvlRead.hEvent;
|
||||||
wait4[2] = _tapOvlWrite.hEvent;
|
wait4[2] = _tapOvlWrite.hEvent; // only included if writeInProgress is true
|
||||||
|
|
||||||
ReadFile(_tap,_tapReadBuf,sizeof(_tapReadBuf),NULL,&_tapOvlRead);
|
ReadFile(_tap,_tapReadBuf,sizeof(_tapReadBuf),NULL,&_tapOvlRead);
|
||||||
bool writeInProgress = false;
|
bool writeInProgress = false;
|
||||||
|
|
|
@ -94,11 +94,6 @@ public:
|
||||||
*/
|
*/
|
||||||
~EthernetTap();
|
~EthernetTap();
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform OS dependent actions on network configuration change detection
|
|
||||||
*/
|
|
||||||
void whack();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the user display name for this connection
|
* Set the user display name for this connection
|
||||||
*
|
*
|
||||||
|
@ -245,6 +240,7 @@ private:
|
||||||
std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
|
std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
|
||||||
Mutex _injectPending_m;
|
Mutex _injectPending_m;
|
||||||
volatile bool _run;
|
volatile bool _run;
|
||||||
|
volatile bool _initialized;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ const char *Network::statusString(const Status s)
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
switch(s) {
|
switch(s) {
|
||||||
|
case NETWORK_INITIALIZING: return "INITIALIZING";
|
||||||
case NETWORK_WAITING_FOR_FIRST_AUTOCONF: return "WAITING_FOR_FIRST_AUTOCONF";
|
case NETWORK_WAITING_FOR_FIRST_AUTOCONF: return "WAITING_FOR_FIRST_AUTOCONF";
|
||||||
case NETWORK_OK: return "OK";
|
case NETWORK_OK: return "OK";
|
||||||
case NETWORK_ACCESS_DENIED: return "ACCESS_DENIED";
|
case NETWORK_ACCESS_DENIED: return "ACCESS_DENIED";
|
||||||
|
@ -56,6 +57,8 @@ const char *Network::statusString(const Status s)
|
||||||
|
|
||||||
Network::~Network()
|
Network::~Network()
|
||||||
{
|
{
|
||||||
|
Thread::join(_setupThread);
|
||||||
|
|
||||||
std::string devPersistentId(_tap->persistentId());
|
std::string devPersistentId(_tap->persistentId());
|
||||||
delete _tap;
|
delete _tap;
|
||||||
|
|
||||||
|
@ -73,46 +76,50 @@ Network::~Network()
|
||||||
|
|
||||||
SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t id)
|
SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t id)
|
||||||
{
|
{
|
||||||
// Tag to identify tap device -- used on some OSes like Windows
|
/* We construct Network via a static method to ensure that it is immediately
|
||||||
char tag[32];
|
* wrapped in a SharedPtr<>. Otherwise if there is traffic on the Ethernet
|
||||||
Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)id);
|
* tap device, a SharedPtr<> wrap can occur in the Ethernet frame handler
|
||||||
|
* that then causes the Network instance to be deleted before it is finished
|
||||||
|
* being constructed. C++ edge cases, how I love thee. */
|
||||||
|
|
||||||
// We construct Network via a static method to ensure that it is immediately
|
|
||||||
// wrapped in a SharedPtr<>. Otherwise if there is traffic on the Ethernet
|
|
||||||
// tap device, a SharedPtr<> wrap can occur in the Ethernet frame handler
|
|
||||||
// that then causes the Network instance to be deleted before it is finished
|
|
||||||
// being constructed. C++ edge cases, how I love thee.
|
|
||||||
SharedPtr<Network> nw(new Network());
|
SharedPtr<Network> nw(new Network());
|
||||||
nw->_id = id;
|
nw->_id = id;
|
||||||
nw->_ready = false; // disable handling of Ethernet frames during construct
|
nw->_mac = renv->identity.address().toMAC();
|
||||||
nw->_r = renv;
|
nw->_r = renv;
|
||||||
nw->_tap = new EthernetTap(renv,tag,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr());
|
nw->_tap = (EthernetTap *)0;
|
||||||
nw->_lastConfigUpdate = 0;
|
nw->_lastConfigUpdate = 0;
|
||||||
nw->_status = NETWORK_WAITING_FOR_FIRST_AUTOCONF;
|
|
||||||
nw->_destroyOnDelete = false;
|
nw->_destroyOnDelete = false;
|
||||||
|
nw->_netconfFailure = NETCONF_FAILURE_NONE;
|
||||||
|
|
||||||
if (nw->controller() == renv->identity.address()) // netconf masters can't really join networks
|
if (nw->controller() == renv->identity.address()) // netconf masters can't really join networks
|
||||||
throw std::runtime_error("cannot join a network for which I am the netconf master");
|
throw std::runtime_error("cannot join a network for which I am the netconf master");
|
||||||
nw->_restoreState();
|
|
||||||
nw->_ready = true; // enable handling of Ethernet frames
|
nw->_setupThread = Thread::start<Network>(nw.ptr());
|
||||||
nw->requestConfiguration();
|
|
||||||
|
|
||||||
return nw;
|
return nw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::setConfiguration(const Dictionary &conf,bool saveToDisk)
|
bool Network::setConfiguration(const Dictionary &conf,bool saveToDisk)
|
||||||
{
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
|
EthernetTap *t = _tap;
|
||||||
|
if (!t) {
|
||||||
|
TRACE("BUG: setConfiguration() called while tap is null!");
|
||||||
|
return false; // can't accept config in initialization state
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SharedPtr<NetworkConfig> newConfig(new NetworkConfig(conf));
|
SharedPtr<NetworkConfig> newConfig(new NetworkConfig(conf));
|
||||||
if ((newConfig->networkId() == _id)&&(newConfig->issuedTo() == _r->identity.address())) {
|
if ((newConfig->networkId() == _id)&&(newConfig->issuedTo() == _r->identity.address())) {
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
_config = newConfig;
|
_config = newConfig;
|
||||||
|
|
||||||
if (newConfig->staticIps().size())
|
if (newConfig->staticIps().size())
|
||||||
_tap->setIps(newConfig->staticIps());
|
t->setIps(newConfig->staticIps());
|
||||||
_tap->setDisplayName((std::string("ZeroTier One [") + newConfig->name() + "]").c_str());
|
t->setDisplayName((std::string("ZeroTier One [") + newConfig->name() + "]").c_str());
|
||||||
|
|
||||||
_lastConfigUpdate = Utils::now();
|
_lastConfigUpdate = Utils::now();
|
||||||
_status = NETWORK_OK;
|
_netconfFailure = NETCONF_FAILURE_NONE;
|
||||||
|
|
||||||
if (saveToDisk) {
|
if (saveToDisk) {
|
||||||
std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf");
|
std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf");
|
||||||
|
@ -122,6 +129,8 @@ void Network::setConfiguration(const Dictionary &conf,bool saveToDisk)
|
||||||
Utils::lockDownFile(confPath.c_str(),false);
|
Utils::lockDownFile(confPath.c_str(),false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
LOG("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id);
|
LOG("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id);
|
||||||
}
|
}
|
||||||
|
@ -130,10 +139,15 @@ void Network::setConfiguration(const Dictionary &conf,bool saveToDisk)
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
LOG("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
|
LOG("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::requestConfiguration()
|
void Network::requestConfiguration()
|
||||||
{
|
{
|
||||||
|
if (!_tap)
|
||||||
|
return; // don't bother requesting until we are initialized
|
||||||
|
|
||||||
if (controller() == _r->identity.address()) {
|
if (controller() == _r->identity.address()) {
|
||||||
// netconf master cannot be a member of its own nets
|
// netconf master cannot be a member of its own nets
|
||||||
LOG("unable to request network configuration for network %.16llx: I am the network master, cannot query self",(unsigned long long)_id);
|
LOG("unable to request network configuration for network %.16llx: I am the network master, cannot query self",(unsigned long long)_id);
|
||||||
|
@ -190,6 +204,7 @@ bool Network::isAllowed(const Address &peer) const
|
||||||
void Network::clean()
|
void Network::clean()
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
if ((_config)&&(_config->isOpen())) {
|
if ((_config)&&(_config->isOpen())) {
|
||||||
// Open (public) networks do not track certs or cert pushes at all.
|
// Open (public) networks do not track certs or cert pushes at all.
|
||||||
_membershipCertificates.clear();
|
_membershipCertificates.clear();
|
||||||
|
@ -215,7 +230,7 @@ void Network::clean()
|
||||||
|
|
||||||
void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
|
void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
|
||||||
{
|
{
|
||||||
if (!((Network *)arg)->isUp())
|
if (((Network *)arg)->status() != NETWORK_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const RuntimeEnvironment *_r = ((Network *)arg)->_r;
|
const RuntimeEnvironment *_r = ((Network *)arg)->_r;
|
||||||
|
@ -250,6 +265,31 @@ void Network::_pushMembershipCertificate(const Address &peer,bool force,uint64_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Network::threadMain()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Setup thread -- this exits when tap is constructed. It's here
|
||||||
|
// because opening the tap can take some time on some platforms.
|
||||||
|
char tag[32];
|
||||||
|
Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)_id);
|
||||||
|
_tap = new EthernetTap(_r,tag,_mac,ZT_IF_MTU,&_CBhandleTapData,this);
|
||||||
|
} catch (std::exception &exc) {
|
||||||
|
LOG("network %.16llx failed to initialize: %s",_id,exc.what());
|
||||||
|
_netconfFailure = NETCONF_FAILURE_INIT_FAILED;
|
||||||
|
} catch ( ... ) {
|
||||||
|
LOG("network %.16llx failed to initialize: unknown error",_id);
|
||||||
|
_netconfFailure = NETCONF_FAILURE_INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_restoreState();
|
||||||
|
requestConfiguration();
|
||||||
|
} catch ( ... ) {
|
||||||
|
TRACE("BUG: exception in network setup thread in _restoreState() or requestConfiguration()!");
|
||||||
|
_lastConfigUpdate = 0; // call requestConfiguration() again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Network::_restoreState()
|
void Network::_restoreState()
|
||||||
{
|
{
|
||||||
if (!_id)
|
if (!_id)
|
||||||
|
|
163
node/Network.hpp
163
node/Network.hpp
|
@ -53,6 +53,7 @@
|
||||||
#include "BandwidthAccount.hpp"
|
#include "BandwidthAccount.hpp"
|
||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
#include "CertificateOfMembership.hpp"
|
#include "CertificateOfMembership.hpp"
|
||||||
|
#include "Thread.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -91,6 +92,9 @@ private:
|
||||||
* If there is no saved state, a dummy .conf is created on disk to remember
|
* If there is no saved state, a dummy .conf is created on disk to remember
|
||||||
* this network across restarts.
|
* this network across restarts.
|
||||||
*
|
*
|
||||||
|
* This can be a time consuming operation on some platforms (cough Windows
|
||||||
|
* cough).
|
||||||
|
*
|
||||||
* @param renv Runtime environment
|
* @param renv Runtime environment
|
||||||
* @param id Network ID
|
* @param id Network ID
|
||||||
* @return Reference counted pointer to new network
|
* @return Reference counted pointer to new network
|
||||||
|
@ -109,10 +113,12 @@ public:
|
||||||
*/
|
*/
|
||||||
enum Status
|
enum Status
|
||||||
{
|
{
|
||||||
|
NETWORK_INITIALIZING,
|
||||||
NETWORK_WAITING_FOR_FIRST_AUTOCONF,
|
NETWORK_WAITING_FOR_FIRST_AUTOCONF,
|
||||||
NETWORK_OK,
|
NETWORK_OK,
|
||||||
NETWORK_ACCESS_DENIED,
|
NETWORK_ACCESS_DENIED,
|
||||||
NETWORK_NOT_FOUND
|
NETWORK_NOT_FOUND,
|
||||||
|
NETWORK_INITIALIZATION_FAILED
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,11 +133,6 @@ public:
|
||||||
*/
|
*/
|
||||||
inline uint64_t id() const throw() { return _id; }
|
inline uint64_t id() const throw() { return _id; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Ethernet tap
|
|
||||||
*/
|
|
||||||
inline EthernetTap &tap() throw() { return *_tap; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Address of network's controlling node
|
* @return Address of network's controlling node
|
||||||
*/
|
*/
|
||||||
|
@ -155,7 +156,10 @@ public:
|
||||||
inline bool updateMulticastGroups()
|
inline bool updateMulticastGroups()
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
return _tap->updateMulticastGroups(_multicastGroups);
|
EthernetTap *t = _tap;
|
||||||
|
if (t)
|
||||||
|
return _tap->updateMulticastGroups(_multicastGroups);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -173,10 +177,34 @@ public:
|
||||||
* This is called by PacketDecoder when an update comes over the wire, or
|
* This is called by PacketDecoder when an update comes over the wire, or
|
||||||
* internally when an old config is reloaded from disk.
|
* internally when an old config is reloaded from disk.
|
||||||
*
|
*
|
||||||
|
* This also cancels any netconf failure flags.
|
||||||
|
*
|
||||||
|
* The network can't accept configuration when in INITIALIZATION state,
|
||||||
|
* and so in that state this will just return false.
|
||||||
|
*
|
||||||
* @param conf Configuration in key/value dictionary form
|
* @param conf Configuration in key/value dictionary form
|
||||||
* @param saveToDisk IF true (default), write config to disk
|
* @param saveToDisk IF true (default), write config to disk
|
||||||
|
* @return True if configuration was accepted
|
||||||
*/
|
*/
|
||||||
void setConfiguration(const Dictionary &conf,bool saveToDisk = true);
|
bool setConfiguration(const Dictionary &conf,bool saveToDisk = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set netconf failure to 'access denied'.
|
||||||
|
*/
|
||||||
|
inline void setAccessDenied()
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
_netconfFailure = NETCONF_FAILURE_ACCESS_DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set netconf failure to 'not found'.
|
||||||
|
*/
|
||||||
|
inline void setNotFound()
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
_netconfFailure = NETCONF_FAILURE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Causes this network to request an updated configuration from its master node now
|
* Causes this network to request an updated configuration from its master node now
|
||||||
|
@ -223,16 +251,6 @@ public:
|
||||||
*/
|
*/
|
||||||
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Force this network's status to a particular state based on config reply
|
|
||||||
*/
|
|
||||||
inline void forceStatusTo(const Status s)
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
_status = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Status of this network
|
* @return Status of this network
|
||||||
*/
|
*/
|
||||||
|
@ -240,17 +258,20 @@ public:
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
return _status;
|
if (_tap) {
|
||||||
}
|
switch(_netconfFailure) {
|
||||||
|
case NETCONF_FAILURE_ACCESS_DENIED:
|
||||||
/**
|
return NETWORK_ACCESS_DENIED;
|
||||||
* @return True if this network is in "OK" status and can accept traffic from us
|
case NETCONF_FAILURE_NOT_FOUND:
|
||||||
*/
|
return NETWORK_NOT_FOUND;
|
||||||
inline bool isUp() const
|
case NETCONF_FAILURE_NONE:
|
||||||
throw()
|
if (_lastConfigUpdate > 0)
|
||||||
{
|
return NETWORK_OK;
|
||||||
Mutex::Lock _l(_lock);
|
else return NETWORK_WAITING_FOR_FIRST_AUTOCONF;
|
||||||
return ((_config)&&(_status == NETWORK_OK)&&(_ready));
|
}
|
||||||
|
} else if (_netconfFailure == NETCONF_FAILURE_INIT_FAILED)
|
||||||
|
return NETWORK_INITIALIZATION_FAILED;
|
||||||
|
else return NETWORK_INITIALIZING;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -307,6 +328,73 @@ public:
|
||||||
return _config;
|
return _config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread main method; do not call elsewhere
|
||||||
|
*/
|
||||||
|
void threadMain()
|
||||||
|
throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject a frame into tap (if it's created)
|
||||||
|
*
|
||||||
|
* @param from Origin MAC
|
||||||
|
* @param to Destination MC
|
||||||
|
* @param etherType Ethernet frame type
|
||||||
|
* @param data Frame data
|
||||||
|
* @param len Frame length
|
||||||
|
*/
|
||||||
|
inline void tapPut(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
||||||
|
{
|
||||||
|
EthernetTap *t = _tap;
|
||||||
|
if (t)
|
||||||
|
t->put(from,to,etherType,data,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject a frame into tap with local MAC as destination MAC (if it's created)
|
||||||
|
*
|
||||||
|
* @param from Origin MAC
|
||||||
|
* @param etherType Ethernet frame type
|
||||||
|
* @param data Frame data
|
||||||
|
* @param len Frame length
|
||||||
|
*/
|
||||||
|
inline void tapPut(const MAC &from,unsigned int etherType,const void *data,unsigned int len)
|
||||||
|
{
|
||||||
|
EthernetTap *t = _tap;
|
||||||
|
if (t)
|
||||||
|
t->put(from,t->mac(),etherType,data,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Tap device name or empty string if still initializing
|
||||||
|
*/
|
||||||
|
inline std::string tapDeviceName() const
|
||||||
|
{
|
||||||
|
EthernetTap *t = _tap;
|
||||||
|
if (t)
|
||||||
|
return t->deviceName();
|
||||||
|
else return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Ethernet MAC address for this network's local interface
|
||||||
|
*/
|
||||||
|
inline const MAC &mac() const
|
||||||
|
{
|
||||||
|
return _mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Set of currently assigned IP addresses
|
||||||
|
*/
|
||||||
|
inline std::set<InetAddress> ips() const
|
||||||
|
{
|
||||||
|
EthernetTap *t = _tap;
|
||||||
|
if (t)
|
||||||
|
return t->ips();
|
||||||
|
return std::set<InetAddress>();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
|
static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
|
||||||
|
|
||||||
|
@ -315,22 +403,23 @@ private:
|
||||||
void _dumpMulticastCerts();
|
void _dumpMulticastCerts();
|
||||||
|
|
||||||
uint64_t _id;
|
uint64_t _id;
|
||||||
|
MAC _mac;
|
||||||
const RuntimeEnvironment *_r;
|
const RuntimeEnvironment *_r;
|
||||||
|
EthernetTap *volatile _tap;
|
||||||
EthernetTap *_tap;
|
|
||||||
std::set<MulticastGroup> _multicastGroups;
|
std::set<MulticastGroup> _multicastGroups;
|
||||||
|
|
||||||
std::map< std::pair<Address,MulticastGroup>,BandwidthAccount > _multicastRateAccounts;
|
std::map< std::pair<Address,MulticastGroup>,BandwidthAccount > _multicastRateAccounts;
|
||||||
std::map<Address,CertificateOfMembership> _membershipCertificates;
|
std::map<Address,CertificateOfMembership> _membershipCertificates;
|
||||||
std::map<Address,uint64_t> _lastPushedMembershipCertificate;
|
std::map<Address,uint64_t> _lastPushedMembershipCertificate;
|
||||||
SharedPtr<NetworkConfig> _config;
|
SharedPtr<NetworkConfig> _config;
|
||||||
|
|
||||||
volatile uint64_t _lastConfigUpdate;
|
volatile uint64_t _lastConfigUpdate;
|
||||||
volatile Status _status;
|
|
||||||
volatile bool _destroyOnDelete;
|
volatile bool _destroyOnDelete;
|
||||||
volatile bool _ready;
|
volatile enum {
|
||||||
|
NETCONF_FAILURE_NONE,
|
||||||
|
NETCONF_FAILURE_ACCESS_DENIED,
|
||||||
|
NETCONF_FAILURE_NOT_FOUND,
|
||||||
|
NETCONF_FAILURE_INIT_FAILED
|
||||||
|
} _netconfFailure;
|
||||||
|
Thread _setupThread;
|
||||||
Mutex _lock;
|
Mutex _lock;
|
||||||
|
|
||||||
AtomicCounter __refCount;
|
AtomicCounter __refCount;
|
||||||
|
|
|
@ -545,7 +545,6 @@ Node::ReasonForTermination Node::run()
|
||||||
LOG("netconf fingerprint change: %.16llx != %.16llx, resyncing with network",networkConfigurationFingerprint,fp);
|
LOG("netconf fingerprint change: %.16llx != %.16llx, resyncing with network",networkConfigurationFingerprint,fp);
|
||||||
networkConfigurationFingerprint = fp;
|
networkConfigurationFingerprint = fp;
|
||||||
resynchronize = true;
|
resynchronize = true;
|
||||||
_r->nc->whackAllTaps(); // call whack() on all tap devices -- hack, might go away
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,14 +115,6 @@ NodeConfig::~NodeConfig()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeConfig::whackAllTaps()
|
|
||||||
{
|
|
||||||
std::vector< SharedPtr<Network> > nwlist;
|
|
||||||
Mutex::Lock _l(_networks_m);
|
|
||||||
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
|
|
||||||
n->second->tap().whack();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeConfig::clean()
|
void NodeConfig::clean()
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_networks_m);
|
Mutex::Lock _l(_networks_m);
|
||||||
|
@ -205,7 +197,7 @@ std::vector<std::string> NodeConfig::execute(const char *command)
|
||||||
_P("200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>");
|
_P("200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>");
|
||||||
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.begin());nw!=_networks.end();++nw) {
|
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.begin());nw!=_networks.end();++nw) {
|
||||||
std::string tmp;
|
std::string tmp;
|
||||||
std::set<InetAddress> ips(nw->second->tap().ips());
|
std::set<InetAddress> ips(nw->second->ips());
|
||||||
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 (tmp.length())
|
if (tmp.length())
|
||||||
tmp.push_back(',');
|
tmp.push_back(',');
|
||||||
|
@ -219,13 +211,14 @@ std::vector<std::string> NodeConfig::execute(const char *command)
|
||||||
age = 0;
|
age = 0;
|
||||||
age /= 1000;
|
age /= 1000;
|
||||||
|
|
||||||
|
std::string dn(nw->second->tapDeviceName());
|
||||||
_P("200 listnetworks %.16llx %s %s %lld %s %s %s",
|
_P("200 listnetworks %.16llx %s %s %lld %s %s %s",
|
||||||
(unsigned long long)nw->first,
|
(unsigned long long)nw->first,
|
||||||
((nconf) ? nconf->name().c_str() : "?"),
|
((nconf) ? nconf->name().c_str() : "?"),
|
||||||
Network::statusString(nw->second->status()),
|
Network::statusString(nw->second->status()),
|
||||||
age,
|
age,
|
||||||
((nconf) ? (nconf->isOpen() ? "public" : "private") : "?"),
|
((nconf) ? (nconf->isOpen() ? "public" : "private") : "?"),
|
||||||
nw->second->tap().deviceName().c_str(),
|
(dn.length() > 0) ? dn.c_str() : "?",
|
||||||
((tmp.length() > 0) ? tmp.c_str() : "-"));
|
((tmp.length() > 0) ? tmp.c_str() : "-"));
|
||||||
}
|
}
|
||||||
} else if (cmd[0] == "join") {
|
} else if (cmd[0] == "join") {
|
||||||
|
|
|
@ -90,11 +90,6 @@ public:
|
||||||
return nwlist;
|
return nwlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Call whack() on all networks' tap devices
|
|
||||||
*/
|
|
||||||
void whackAllTaps();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform cleanup and possibly update saved state
|
* Perform cleanup and possibly update saved state
|
||||||
*/
|
*/
|
||||||
|
@ -117,8 +112,11 @@ public:
|
||||||
{
|
{
|
||||||
std::set<std::string> tapDevs;
|
std::set<std::string> tapDevs;
|
||||||
Mutex::Lock _l(_networks_m);
|
Mutex::Lock _l(_networks_m);
|
||||||
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
|
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
|
||||||
tapDevs.insert(n->second->tap().deviceName());
|
std::string dn(n->second->tapDeviceName());
|
||||||
|
if (dn.length())
|
||||||
|
tapDevs.insert(dn);
|
||||||
|
}
|
||||||
return tapDevs;
|
return tapDevs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
|
||||||
} else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
} else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
||||||
SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
if ((network)&&(network->controller() == source()))
|
if ((network)&&(network->controller() == source()))
|
||||||
network->forceStatusTo(Network::NETWORK_NOT_FOUND);
|
network->setNotFound();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Packet::ERROR_IDENTITY_COLLISION:
|
case Packet::ERROR_IDENTITY_COLLISION:
|
||||||
|
@ -154,7 +154,7 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
|
||||||
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
|
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
|
||||||
SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
if ((network)&&(network->controller() == source()))
|
if ((network)&&(network->controller() == source()))
|
||||||
network->forceStatusTo(Network::NETWORK_ACCESS_DENIED);
|
network->setAccessDenied();
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -416,7 +416,7 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer>
|
||||||
unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
|
unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
|
||||||
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
|
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
|
||||||
if (network->config()->permitsEtherType(etherType)) {
|
if (network->config()->permitsEtherType(etherType)) {
|
||||||
network->tap().put(source().toMAC(),network->tap().mac(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD);
|
network->tapPut(source().toMAC(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD);
|
||||||
} else {
|
} else {
|
||||||
TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id());
|
TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id());
|
||||||
return true;
|
return true;
|
||||||
|
@ -677,7 +677,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
|
||||||
TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str());
|
TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str());
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
network->tap().put(sourceMac,dest.mac(),etherType,frame,frameLen);
|
network->tapPut(sourceMac,dest.mac(),etherType,frame,frameLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,12 +86,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||||
if (!nconf)
|
if (!nconf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (to == network->tap().mac()) {
|
if (to == network->mac()) {
|
||||||
LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tap().deviceName().c_str());
|
LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (from != network->mac()) {
|
||||||
if (from != network->tap().mac()) {
|
|
||||||
LOG("ignored tap: %s -> %s %s (bridging not supported)",from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
LOG("ignored tap: %s -> %s %s (bridging not supported)",from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue