From e5f7c55c5495369c091ab5aec449638d7abd5a50 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 12:34:35 -0700 Subject: Documentation in Packet, more work on path push, and clean up ancient legacy support code in Switch. --- node/IncomingPacket.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7e2bcdaa..f45a1279 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -595,9 +595,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); return true; } - } - - if (to != network->mac()) { + } else if (to != network->mac()) { if (!network->permitsBridging(RR->identity.address())) { TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); return true; -- cgit v1.2.3 From fad9dff2db26662e1496329057884b3b928cb1c9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:05:04 -0700 Subject: Almost all of GitHub issue #180 --- node/IncomingPacket.cpp | 6 ++++++ node/IncomingPacket.hpp | 1 + node/Node.hpp | 10 ++++++++++ node/Switch.cpp | 10 +++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index f45a1279..7634f54f 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -84,6 +84,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer); case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer); + case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer); } } else { RR->sw->requestWhois(source()); @@ -882,6 +883,11 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share return true; } +bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer) +{ + return true; +} + void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid) { Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index 174fa38d..3bf7737d 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -121,6 +121,7 @@ private: bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer); // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to join void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid); diff --git a/node/Node.hpp b/node/Node.hpp index 2d2898b5..5c7cfae2 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -43,6 +43,7 @@ #include "Mutex.hpp" #include "MAC.hpp" #include "Network.hpp" +#include "Path.hpp" #undef TRACE #ifdef ZT_TRACE @@ -171,6 +172,12 @@ public: return nw; } + inline std::vector directPaths() const + { + Mutex::Lock _l(_directPaths_m); + return _directPaths; + } + inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast(this),_uPtr,name,data,len,(int)secure) == 0); } inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); } inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast(this),_uPtr,name,(const void *)0,0,0); } @@ -236,6 +243,9 @@ private: std::vector< std::pair< uint64_t, SharedPtr > > _networks; Mutex _networks_m; + std::vector _directPaths; + Mutex _directPaths_m; + Mutex _backgroundTasksLock; uint64_t _now; diff --git a/node/Switch.cpp b/node/Switch.cpp index 891c4a45..7600624f 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -733,10 +733,16 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (peer) { const uint64_t now = RR->node->now(); + if (nwid) { + // If this packet has an associated network, give the peer additional hints for direct connectivity + peer->pushDirectPaths(RR,RR->node->directPaths(),now,false); + } + RemotePath *viaPath = peer->getBestPath(now); if (!viaPath) { SharedPtr relay; + // See if this network has a preferred relay (if packet has an associated network) if (nwid) { SharedPtr network(RR->node->network(nwid)); if (network) { @@ -754,6 +760,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) } } + // Otherwise relay off a root server if (!relay) relay = RR->topology->getBestRoot(); @@ -770,7 +777,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (viaPath->send(RR,tmp.data(),chunkSize,now)) { if (chunkSize < tmp.size()) { - // Too big for one bite, fragment the rest + // Too big for one packet, fragment the rest unsigned int fragStart = chunkSize; unsigned int remaining = tmp.size() - chunkSize; unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)); @@ -786,6 +793,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) remaining -= chunkSize; } } + return true; } } else { -- cgit v1.2.3 From 79e9a8bcc26491a43577082ec7edb86020f7ec00 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:28:48 -0700 Subject: Almost everything for GitHub issue #180 except direct path map setup. --- node/Constants.hpp | 10 ---------- node/IncomingPacket.cpp | 30 ++++++++++++++++++++++++++++++ node/Node.cpp | 16 +--------------- node/Node.hpp | 1 - node/Packet.hpp | 21 +-------------------- node/Peer.cpp | 6 +++++- node/Switch.cpp | 24 ++---------------------- node/Switch.hpp | 2 -- 8 files changed, 39 insertions(+), 71 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index 1f0d8426..d15fef13 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -304,16 +304,6 @@ */ #define ZT_ANTIRECURSION_HISTORY_SIZE 16 -/** - * How often to send LAN beacons - */ -#define ZT_BEACON_INTERVAL 30000 - -/** - * Do not respond to any beacon more often than this - */ -#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500 - /** * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7634f54f..87669ba7 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -885,6 +885,36 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer) { + try { + unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + + while (count) { // if ptr overflows Buffer will throw + unsigned int flags = (*this)[ptr++]; + /*int metric = (*this)[ptr++];*/ ++ptr; + unsigned int extLen = at(ptr); ptr += 2; + ptr += extLen; // unused right now + unsigned int addrType = (*this)[ptr++]; + unsigned int addrLen = (*this)[ptr++]; + switch(addrType) { + case 4: { + InetAddress a(field(ptr,4),4,at(ptr + 4)); + if ((flags & (0x01 | 0x02)) == 0) + peer->attemptToContactAt(RR,a,RR->node->now()); + } break; + case 6: { + InetAddress a(field(ptr,16),16,at(ptr + 16)); + if ((flags & (0x01 | 0x02)) == 0) + peer->attemptToContactAt(RR,a,RR->node->now()); + } break; + } + ptr += addrLen; + } + } catch (std::exception &exc) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } return true; } diff --git a/node/Node.cpp b/node/Node.cpp index 8eacf753..7c70db97 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -78,8 +78,7 @@ Node::Node( _networks_m(), _now(now), _lastPingCheck(0), - _lastHousekeepingRun(0), - _lastBeacon(0) + _lastHousekeepingRun(0) { _newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR; _newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR; @@ -269,19 +268,6 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next _online = ((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT); if (oldOnline != _online) postEvent(_online ? ZT1_EVENT_ONLINE : ZT1_EVENT_OFFLINE); - - // Send LAN beacons - if ((now - _lastBeacon) >= ZT_BEACON_INTERVAL) { - _lastBeacon = now; - char beacon[13]; - void *p = beacon; - *(reinterpret_cast(p)) = RR->prng->next32(); - p = beacon + 4; - *(reinterpret_cast(p)) = RR->prng->next32(); - RR->identity.address().copyTo(beacon + 8,5); - RR->antiRec->logOutgoingZT(beacon,13); - putPacket(ZT_DEFAULTS.v4Broadcast,beacon,13); - } } catch ( ... ) { return ZT1_RESULT_FATAL_ERROR_INTERNAL; } diff --git a/node/Node.hpp b/node/Node.hpp index 5c7cfae2..cd8d68fc 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -251,7 +251,6 @@ private: uint64_t _now; uint64_t _lastPingCheck; uint64_t _lastHousekeepingRun; - uint64_t _lastBeacon; unsigned int _newestVersionSeen[3]; // major, minor, revision bool _online; }; diff --git a/node/Packet.hpp b/node/Packet.hpp index 9b86709a..50471b06 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -233,16 +233,6 @@ */ #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD -/** - * DEPRECATED: length of LAN beacon packets - */ -#define ZT_PROTO_BEACON_LENGTH 13 - -/** - * DEPRECATED: index of address in a LAN beacon - */ -#define ZT_PROTO_BEACON_IDX_ADDRESS 8 - // Destination address types from HELLO, OK(HELLO), and other message types #define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0 #define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused @@ -388,16 +378,6 @@ namespace ZeroTier { * * For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever * sent in the clear, as it's the "here is my public key" message. - * - * Beacon format and beacon packets: - * <[8] 8 random bytes> - * <[5] sender ZT address> - * - * A beacon is a 13-byte packet containing only the address of the sender. - * Receiving peers may or may not respond to beacons with a HELLO or other - * message to initiate direct communication. - * - * Beacons may be used for direct LAN announcement or NAT traversal. */ class Packet : public Buffer { @@ -901,6 +881,7 @@ public: * <[2] length of extended path characteristics or 0 for none> * <[...] extended path characteristics> * <[1] address type> + * <[1] address length in bytes> * <[...] address> * * Path record flags: diff --git a/node/Peer.cpp b/node/Peer.cpp index ec04357a..225fbdef 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -210,9 +210,12 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force) { - if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { + if ((!dps.empty())&&(((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { _lastDirectPathPush = now; + TRACE("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + printf("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); @@ -254,6 +257,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0)); outp.append((uint16_t)0); outp.append(addressType); + outp.append((addressType == 4) ? 6 : 18); outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); outp.append((uint16_t)p->address().port()); diff --git a/node/Switch.cpp b/node/Switch.cpp index 7600624f..6a0b07cd 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -49,8 +49,7 @@ namespace ZeroTier { Switch::Switch(const RuntimeEnvironment *renv) : - RR(renv), - _lastBeacon(0) + RR(renv) { } @@ -61,9 +60,7 @@ Switch::~Switch() void Switch::onRemotePacket(const InetAddress &fromAddr,const void *data,unsigned int len) { try { - if (len == ZT_PROTO_BEACON_LENGTH) { - _handleBeacon(fromAddr,Buffer(data,len)); - } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { + if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { if (((const unsigned char *)data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) { _handleRemotePacketFragment(fromAddr,data,len); } else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { @@ -696,23 +693,6 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat } } -void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer &data) -{ - Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - if (beaconAddr == RR->identity.address()) - return; - SharedPtr peer(RR->topology->getPeer(beaconAddr)); - if (peer) { - const uint64_t now = RR->node->now(); - if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) { - _lastBeacon = now; - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP); - outp.armor(peer->key(),false); - RR->node->putPacket(fromAddr,outp.data(),outp.size()); - } - } -} - Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted) { SharedPtr root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); diff --git a/node/Switch.hpp b/node/Switch.hpp index 0ba4c138..af6e5938 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -183,12 +183,10 @@ public: private: void _handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len); void _handleRemotePacketHead(const InetAddress &fromAddr,const void *data,unsigned int len); - void _handleBeacon(const InetAddress &fromAddr,const Buffer &data); Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted); bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid); const RuntimeEnvironment *const RR; - volatile uint64_t _lastBeacon; // Outsanding WHOIS requests and how many retries they've undergone struct WhoisRequest -- cgit v1.2.3 From f881cdd7675fc861ea6696837ab6e8ea650c036e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 17:22:37 -0700 Subject: Add new .h file to VS build, and Windows side of local interface address enumeration. --- node/IncomingPacket.cpp | 8 +++-- service/OneService.cpp | 43 +++++++++++++++++++++++-- windows/ZeroTierOne/ZeroTierOne.vcxproj | 1 + windows/ZeroTierOne/ZeroTierOne.vcxproj.filters | 3 ++ 4 files changed, 51 insertions(+), 4 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 87669ba7..c1594f81 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -899,13 +899,17 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ((flags & (0x01 | 0x02)) == 0) + if ((flags & (0x01 | 0x02)) == 0) { + printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); + } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ((flags & (0x01 | 0x02)) == 0) + if ((flags & (0x01 | 0x02)) == 0) { + printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); + } } break; } ptr += addrLen; diff --git a/service/OneService.cpp b/service/OneService.cpp index 109050bf..527ac1b0 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -75,7 +75,11 @@ class SqliteNetworkController; #endif // ZT_ENABLE_NETWORK_CONTROLLER #ifdef __WINDOWS__ +#include +#include #include +#include +#include #else #include #include @@ -614,10 +618,45 @@ public: { Mutex::Lock _l(_taps_m); for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t) - ztDevices.push_back(t->second->deviceName()); + ztDevices.push_back(t->second->luid()); } - // TODO + char aabuf[16384]; + ULONG aalen = sizeof(aabuf); + if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast(aabuf),&aalen) == NO_ERROR) { + PIP_ADAPTER_ADDRESSES a = reinterpret_cast(aabuf); + while (a) { + bool isZT = false; + for(std::vector::const_iterator d(ztDevices.begin());d!=ztDevices.end();++d) { + if (a->Luid.Value == d->Value) { + isZT = true; + break; + } + } + if (!isZT) { + PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; + while (ua) { + InetAddress ip(ua->Address.lpSockaddr); + if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { + switch(ip.ipScope()) { + case InetAddress::IP_SCOPE_LINK_LOCAL: + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); + break; + default: + break; + } + } + ua = ua->Next; + } + } + a = a->Next; + } + } #endif // __WINDOWS__ } diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index acd4c7fe..554669b6 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -94,6 +94,7 @@ + diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index 9337a88e..f36b5dc0 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -344,6 +344,9 @@ Header Files\osdep + + Header Files\node + -- cgit v1.2.3 From 6da9d2d36f5313848d35a4850aaf0f3c5f11d98b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 17:23:22 -0700 Subject: Remove debug printf(). --- node/IncomingPacket.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c1594f81..ab771b7e 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -900,14 +900,12 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); if ((flags & (0x01 | 0x02)) == 0) { - printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); if ((flags & (0x01 | 0x02)) == 0) { - printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); } } break; -- cgit v1.2.3 From f398952a6c03574e5947f6dfe5ea0f7b0f0b5224 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 08:14:41 -0700 Subject: Revert some bad docs in Packet -- I think we will still use that. Also rename addMembershipCertificate to more security-descriptive validateAndAddMembershipCertificate, give it a return value, and drop unused force parameter. --- node/IncomingPacket.cpp | 8 ++++---- node/Network.cpp | 54 +++++++++++++++++++++++++------------------------ node/Network.hpp | 4 ++-- node/Packet.hpp | 6 ------ 4 files changed, 34 insertions(+), 38 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index ab771b7e..80159194 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -429,7 +429,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS); SharedPtr network(RR->node->network(nwid)); if ((network)&&(com.hasRequiredFields())) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } if ((flags & 0x02) != 0) { @@ -558,7 +558,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } if (!network->isAllowed(peer->address())) { @@ -648,7 +648,7 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); if (network) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } } @@ -806,7 +806,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM); if (com.hasRequiredFields()) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } // Check membership after we've read any included COM, since diff --git a/node/Network.cpp b/node/Network.cpp index 4414e4d1..84b8d320 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -267,53 +267,55 @@ void Network::requestConfiguration() RR->sw->send(outp,true,_id); } -void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept) +bool Network::validateAndAddMembershipCertificate(const CertificateOfMembership &cert) { if (!cert) // sanity check - return; + return false; Mutex::Lock _l(_lock); CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()]; // Nothing to do if the cert hasn't changed -- we get duplicates due to zealous cert pushing if (old == cert) - return; + return true; // but if it's a duplicate of one we already accepted, return is 'true' // Check signature, log and return if cert is invalid - if (!forceAccept) { - if (cert.signedBy() != controller()) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; + if (cert.signedBy() != controller()) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signer + } + + if (cert.signedBy() == RR->identity.address()) { + + // We are the controller: RR->identity.address() == controller() == cert.signedBy() + // So, verify that we signed th cert ourself + if (!cert.verify(RR->identity)) { + TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature } - if (cert.signedBy() == RR->identity.address()) { - // We are the controller: RR->identity.address() == controller() == cert.signedBy() - // So, verify that we signed th cert ourself - if (!cert.verify(RR->identity)) { - TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; - } - } else { + } else { - SharedPtr signer(RR->topology->getPeer(cert.signedBy())); + SharedPtr signer(RR->topology->getPeer(cert.signedBy())); - if (!signer) { - // This would be rather odd, since this is our controller... could happen - // if we get packets before we've gotten config. - RR->sw->requestWhois(cert.signedBy()); - return; - } + if (!signer) { + // This would be rather odd, since this is our controller... could happen + // if we get packets before we've gotten config. + RR->sw->requestWhois(cert.signedBy()); + return false; // signer unknown + } - if (!cert.verify(signer->identity())) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; - } + if (!cert.verify(signer->identity())) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature } } // If we made it past authentication, update cert if (cert.revision() != old.revision()) old = cert; + + return true; } bool Network::peerNeedsOurMembershipCertificate(const Address &to,uint64_t now) diff --git a/node/Network.hpp b/node/Network.hpp index daa4554e..53a22dcd 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -179,9 +179,9 @@ public: * Add or update a membership certificate * * @param cert Certificate of membership - * @param forceAccept If true, accept without validating signature + * @return True if certificate was accepted as valid */ - void addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept); + bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert); /** * Check if we should push membership certificate to a peer, and update last pushed diff --git a/node/Packet.hpp b/node/Packet.hpp index 50471b06..51a241ba 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -680,12 +680,6 @@ public: * * Certificate contains network ID, peer it was issued for, etc. * - * Note that in the current code base, separate COM pushes are not done. - * Instead the "bundled COM" options are utilized in EXT_FRAME and - * MULTICAST_FRAME to push the COM along with frames. This is slightly - * more efficient. But we'll keep this simple message around in case we - * want to use it in the future. - * * OK/ERROR are not generated. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, -- cgit v1.2.3 From c863ff3f02e9d68eb9bea32160d252eaddb7f1f5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 08:54:48 -0700 Subject: A bunch of comments and cleanup, including some to yesterday's direct path pushing changes. Move path viability check to one place, and stop trying to use link-local addresses since they are not reliable. --- include/ZeroTierOne.h | 9 ++++++- node/IncomingPacket.cpp | 66 +++++++++++++++++++++++++++++++++++-------------- node/Node.cpp | 24 +++++++++++------- node/Node.hpp | 2 +- node/Packet.hpp | 7 +++--- node/Path.hpp | 33 +++++++++++++++++++++++++ service/OneService.cpp | 32 +++--------------------- 7 files changed, 112 insertions(+), 61 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 4790795e..446bbc77 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -978,12 +978,19 @@ void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr); * Take care that these are never ZeroTier interface addresses, otherwise * strange things might happen or they simply won't work. * + * This returns a boolean indicating whether or not the address was + * accepted. ZeroTier will only communicate over certain address types + * and (for IP) address classes. Thus it's safe to just dump your OS's + * entire remote IP list (excluding ZeroTier interface IPs) into here + * and let ZeroTier determine which addresses it will use. + * * @param addr Local interface address * @param metric Local interface metric * @param trust How much do you trust the local network under this interface? * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall + * @return Boolean: non-zero if address was accepted and added */ -void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); /** * Clear local interface addresses diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 80159194..7e883221 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -134,6 +134,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr break; case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: { + /* Note: certificates are public so it's safe to push them to anyone + * who asks. We won't communicate unless we also get a certificate + * from the remote that agrees. */ SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if (network) { SharedPtr nconf(network->config2()); @@ -170,8 +173,20 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) { + /* Note: this is the only packet ever sent in the clear, and it's also + * the only packet that we authenticate via a different path. Authentication + * occurs here and is based on the validity of the identity and the + * integrity of the packet's MAC, but it must be done after we check + * the identity since HELLO is a mechanism for learning new identities + * in the first place. */ + try { const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; + if (protoVersion < ZT_PROTO_VERSION_MIN) { + TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO_IDX_REVISION); @@ -179,6 +194,10 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) Identity id; unsigned int destAddrPtr = id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY) + ZT_PROTO_VERB_HELLO_IDX_IDENTITY; + if (source() != id.address()) { + TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } InetAddress destAddr; if (destAddrPtr < size()) { // ZeroTier One < 1.0.3 did not include this field @@ -193,16 +212,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } } - if (source() != id.address()) { - TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - - if (protoVersion < ZT_PROTO_VERSION_MIN) { - TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - SharedPtr peer(RR->topology->getPeer(id.address())); if (peer) { // We already have an identity with this address -- check for collisions @@ -245,12 +254,14 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } else { // We don't already have an identity with this address -- validate and learn it + // Check identity proof of work if (!id.locallyValidate()) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } + // Check packet integrity and authentication SharedPtr newPeer(new Peer(RR->identity,id)); if (!dearmor(newPeer->key())) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); @@ -554,14 +565,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

validateAndAddMembershipCertificate(com); + if (com.hasRequiredFields()) { + if (!network->validateAndAddMembershipCertificate(com)) + comFailed = true; // technically this check is redundant to isAllowed(), but do it anyway for thoroughness + } } - if (!network->isAllowed(peer->address())) { + if ((comFailed)||(!network->isAllowed(peer->address()))) { TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; @@ -647,8 +661,21 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment ptr += com.deserialize(*this,ptr); if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); - if (network) - network->validateAndAddMembershipCertificate(com); + if (network) { + if (network->validateAndAddMembershipCertificate(com)) { + if ((network->isAllowed(peer->address()))&&(network->peerNeedsOurMembershipCertificate(peer->address(),RR->node->now()))) { + // If peer passed our check and we haven't sent it our cert yet, respond + // and push our cert as well for instant authorization setup. + SharedPtr nconf(network->config2()); + if ((nconf)&&(nconf->com())) { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); + nconf->com().serialize(outp); + outp.armor(peer->key(),true); + RR->node->putPacket(_remoteAddress,outp.data(),outp.size()); + } + } + } + } } } @@ -890,24 +917,25 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; while (count) { // if ptr overflows Buffer will throw + // TODO: properly handle blacklisting, support other features... see Packet.hpp. + unsigned int flags = (*this)[ptr++]; /*int metric = (*this)[ptr++];*/ ++ptr; unsigned int extLen = at(ptr); ptr += 2; ptr += extLen; // unused right now unsigned int addrType = (*this)[ptr++]; + unsigned int addrLen = (*this)[ptr++]; switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ((flags & (0x01 | 0x02)) == 0) { + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) peer->attemptToContactAt(RR,a,RR->node->now()); - } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ((flags & (0x01 | 0x02)) == 0) { + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) peer->attemptToContactAt(RR,a,RR->node->now()); - } } break; } ptr += addrLen; diff --git a/node/Node.cpp b/node/Node.cpp index 057dc285..d8bd8910 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -426,12 +426,16 @@ void Node::freeQueryResult(void *qr) ::free(qr); } -void Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) -{ - Mutex::Lock _l(_directPaths_m); - _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); - std::sort(_directPaths.begin(),_directPaths.end()); - _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); +int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + if (Path::isAddressValidForPath(*(reinterpret_cast(addr)))) { + Mutex::Lock _l(_directPaths_m); + _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); + std::sort(_directPaths.begin(),_directPaths.end()); + _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); + return 1; + } + return 0; } void Node::clearLocalInterfaceAddresses() @@ -693,11 +697,13 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) } catch ( ... ) {} } -void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) { try { - reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); - } catch ( ... ) {} + return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); + } catch ( ... ) { + return 0; + } } void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node) diff --git a/node/Node.hpp b/node/Node.hpp index 1c260545..fe31576c 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -104,7 +104,7 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); - void addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); diff --git a/node/Packet.hpp b/node/Packet.hpp index 51a241ba..9787edb7 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -678,9 +678,10 @@ public: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * - * Certificate contains network ID, peer it was issued for, etc. - * - * OK/ERROR are not generated. + * OK/ERROR are not generated, but the recipient should push its network + * membership certificate if the certificate the peer pushed is valid + * and agrees and if it hasn't done so in too long. This ensures instant + * network authentication setup between valid and authorized peers. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, diff --git a/node/Path.hpp b/node/Path.hpp index 80b9a3c0..cd21444b 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -94,6 +94,39 @@ public: inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } + /** + * Check whether this address is valid for a ZeroTier path + * + * This checks the address type and scope against address types and scopes + * that we currently support for ZeroTier communication. + * + * @param a Address to check + * @return True if address is good for ZeroTier path use + */ + static inline bool isAddressValidForPath(const InetAddress &a) + throw() + { + if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) { + switch(a.ipScope()) { + /* Note: we don't do link-local at the moment. Unfortunately these + * cause several issues. The first is that they usually require a + * device qualifier, which we don't handle yet and can't portably + * push in PUSH_DIRECT_PATHS. The second is that some OSes assign + * these very ephemerally or otherwise strangely. So we'll use + * private, pseudo-private, shared (e.g. carrier grade NAT), or + * global IP addresses. */ + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + return true; + default: + return false; + } + } + return false; + } + protected: InetAddress _addr; int _metric; // negative == blacklisted diff --git a/service/OneService.cpp b/service/OneService.cpp index 527ac1b0..bde59d56 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -591,20 +591,8 @@ public: } if (!isZT) { InetAddress ip(ifa->ifa_addr); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { - switch(ip.ipScope()) { - case InetAddress::IP_SCOPE_LINK_LOCAL: - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); - break; - default: - break; - } - } + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); } } ifa = ifa->ifa_next; @@ -637,20 +625,8 @@ public: PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; while (ua) { InetAddress ip(ua->Address.lpSockaddr); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { - switch(ip.ipScope()) { - case InetAddress::IP_SCOPE_LINK_LOCAL: - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); - break; - default: - break; - } - } + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); ua = ua->Next; } } -- cgit v1.2.3 From 778c7e6e703353030e2ea130e3db7cc968a5d53c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:00:34 -0700 Subject: More cleanup to direct path push, comment fixes, etc. --- node/IncomingPacket.cpp | 17 +------- node/Network.hpp | 2 +- node/OutboundMulticast.cpp | 8 +--- node/Packet.hpp | 8 ++-- node/Peer.cpp | 10 +++-- node/Peer.hpp | 8 ++-- node/Switch.cpp | 98 +++++++++++++++++++++++----------------------- node/Switch.hpp | 5 ++- 8 files changed, 71 insertions(+), 85 deletions(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7e883221..d5b4e9e6 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -661,21 +661,8 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment ptr += com.deserialize(*this,ptr); if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); - if (network) { - if (network->validateAndAddMembershipCertificate(com)) { - if ((network->isAllowed(peer->address()))&&(network->peerNeedsOurMembershipCertificate(peer->address(),RR->node->now()))) { - // If peer passed our check and we haven't sent it our cert yet, respond - // and push our cert as well for instant authorization setup. - SharedPtr nconf(network->config2()); - if ((nconf)&&(nconf->com())) { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); - nconf->com().serialize(outp); - outp.armor(peer->key(),true); - RR->node->putPacket(_remoteAddress,outp.data(),outp.size()); - } - } - } - } + if (network) + network->validateAndAddMembershipCertificate(com); } } diff --git a/node/Network.hpp b/node/Network.hpp index 53a22dcd..d7320d46 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -184,7 +184,7 @@ public: bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert); /** - * Check if we should push membership certificate to a peer, and update last pushed + * Check if we should push membership certificate to a peer, AND update last pushed * * If we haven't pushed a cert to this peer in a long enough time, this returns * true and updates the last pushed time. Otherwise it returns false. diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index 5809504a..46116c07 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -102,13 +102,9 @@ void OutboundMulticast::init( void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr) { - SharedPtr network(RR->node->network(_nwid)); - - if (!network) - return; - if (_haveCom) { - if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) { + SharedPtr network(RR->node->network(_nwid)); + if ((network)&&(network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now()))) { _packetWithCom.newInitializationVector(); _packetWithCom.setDestination(toAddr); //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str()); diff --git a/node/Packet.hpp b/node/Packet.hpp index 9787edb7..e84306c2 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -678,10 +678,10 @@ public: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * - * OK/ERROR are not generated, but the recipient should push its network - * membership certificate if the certificate the peer pushed is valid - * and agrees and if it hasn't done so in too long. This ensures instant - * network authentication setup between valid and authorized peers. + * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may + * be pushed at any other time to keep exchanged certificates up to date. + * + * OK/ERROR are not generated. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, diff --git a/node/Peer.cpp b/node/Peer.cpp index 7c936d4a..84aa8bef 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -208,12 +208,13 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) } } -void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force) +void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) { - if ((!dps.empty())&&(((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { + if ((((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { _lastDirectPathPush = now; - TRACE("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + std::vector dps(RR->node->directPaths()); + TRACE("pushing %u direct paths (local interface addresses) to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { @@ -266,7 +267,8 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector if (count) { outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count); - RR->sw->send(outp,true,0); + outp.armor(_key,true); + path->send(RR,outp.data(),outp.size(),now); } } } diff --git a/node/Peer.hpp b/node/Peer.hpp index 2af3b9e6..f5118794 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -179,14 +179,14 @@ public: void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now); /** - * Push direct paths (if within rate limit) + * Push direct paths if we haven't done so in [rate limit] milliseconds * * @param RR Runtime environment - * @param dps Direct paths to me to push to this peer + * @param path Remote path to use to send the push * @param now Current time - * @param force If true, force regardless of when we pushed direct paths last + * @param force If true, push regardless of rate limit */ - void pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force); + void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force); /** * @return All known direct paths to this peer diff --git a/node/Switch.cpp b/node/Switch.cpp index 201f36d1..13070be1 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -162,38 +162,34 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (to[0] == MAC::firstOctetForNetwork(network->id())) { // Destination is another ZeroTier peer on the same network - Address toZT(to.toAddress(network->id())); - if (network->isAllowed(toZT)) { - const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); - if ((fromBridged)||(includeCom)) { - Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); - outp.append(network->id()); - if (includeCom) { - outp.append((unsigned char)0x01); // 0x01 -- COM included - nconf->com().serialize(outp); - } else { - outp.append((unsigned char)0x00); - } - to.appendTo(outp); - from.appendTo(outp); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true,network->id()); + Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this + const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); + if ((fromBridged)||(includeCom)) { + Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(network->id()); + if (includeCom) { + outp.append((unsigned char)0x01); // 0x01 -- COM included + nconf->com().serialize(outp); } else { - Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); - outp.append(network->id()); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true,network->id()); + outp.append((unsigned char)0x00); } - - //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); + to.appendTo(outp); + from.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true,network->id()); } else { - TRACE("%.16llx: UNICAST: %s -> %s etherType==%s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); + Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); + outp.append(network->id()); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true,network->id()); } + //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); + return; } @@ -205,7 +201,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c /* Create an array of up to ZT_MAX_BRIDGE_SPAM recipients for this bridged frame. */ bridges[0] = network->findBridgeTo(to); - if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) { + if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->permitsBridging(bridges[0]))) { /* We have a known bridge route for this MAC, send it there. */ ++numBridges; } else if (!nconf->activeBridges().empty()) { @@ -215,8 +211,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) { // If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all while (ab != nconf->activeBridges().end()) { - if (network->isAllowed(*ab)) // config sanity check - bridges[numBridges++] = *ab; + bridges[numBridges++] = *ab; ++ab; } } else { @@ -225,8 +220,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (ab == nconf->activeBridges().end()) ab = nconf->activeBridges().begin(); if (((unsigned long)RR->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0) { - if (network->isAllowed(*ab)) // config sanity check - bridges[numBridges++] = *ab; + bridges[numBridges++] = *ab; ++ab; } else ++ab; } @@ -703,29 +697,28 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (peer) { const uint64_t now = RR->node->now(); + SharedPtr network; + SharedPtr nconf; if (nwid) { - // If this packet has an associated network, give the peer additional hints for direct connectivity - peer->pushDirectPaths(RR,RR->node->directPaths(),now,false); + network = RR->node->network(nwid); + if (!network) + return false; // we probably just left this network, let its packets die + nconf = network->config2(); + if (!nconf) + return false; // sanity check: unconfigured network? why are we trying to talk to it? } RemotePath *viaPath = peer->getBestPath(now); + SharedPtr relay; if (!viaPath) { - SharedPtr relay; - // See if this network has a preferred relay (if packet has an associated network) - if (nwid) { - SharedPtr network(RR->node->network(nwid)); - if (network) { - SharedPtr nconf(network->config2()); - if (nconf) { - unsigned int latency = ~((unsigned int)0); - for(std::vector< std::pair >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { - if (r->first != peer->address()) { - SharedPtr rp(RR->topology->getPeer(r->first)); - if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) - rp.swap(relay); - } - } + if (nconf) { + unsigned int latency = ~((unsigned int)0); + for(std::vector< std::pair >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { + if (r->first != peer->address()) { + SharedPtr rp(RR->topology->getPeer(r->first)); + if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) + rp.swap(relay); } } } @@ -735,7 +728,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) relay = RR->topology->getBestRoot(); if (!(relay)||(!(viaPath = relay->getBestPath(now)))) - return false; + return false; // no paths, no root servers? + } + + if ((network)&&(relay)&&(network->isAllowed(peer->address()))) { + // Push hints for direct connectivity to this peer if we are relaying + peer->pushDirectPaths(RR,viaPath,now,false); } Packet tmp(packet); diff --git a/node/Switch.hpp b/node/Switch.hpp index af6e5938..95ca362c 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -108,10 +108,13 @@ public: * * Needless to say, the packet's source must be this node. Otherwise it * won't be encrypted right. (This is not used for relaying.) + * + * The network ID should only be specified for frames and other actual + * network traffic. * * @param packet Packet to send * @param encrypt Encrypt packet payload? (always true except for HELLO) - * @param nwid Network ID or 0 if message is not related to a specific network + * @param nwid Related network ID or 0 if message is not in-network traffic */ void send(const Packet &packet,bool encrypt,uint64_t nwid); -- cgit v1.2.3 From 412389ec755528108e0254e75a9cf43fc53e331a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 11:49:38 -0700 Subject: Implement ERROR_UNWATNED_MULTICAST --- node/IncomingPacket.cpp | 5 ++++- node/Multicaster.cpp | 14 ++++++++++++++ node/Multicaster.hpp | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'node/IncomingPacket.cpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index d5b4e9e6..6c3a0932 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -156,7 +156,10 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr } break; case Packet::ERROR_UNWANTED_MULTICAST: { - // TODO: unsubscribe + uint64_t nwid = at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14)); + TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str()); + RR->mc->remove(nwid,mg,peer->address()); } break; default: break; diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 3c105236..33424e4a 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -61,6 +61,20 @@ void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &m } } +void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member) +{ + Mutex::Lock _l(_groups_m); + std::map< std::pair,MulticastGroupStatus >::iterator g(_groups.find(std::pair(nwid,mg))); + if (g != _groups.end()) { + for(std::vector::iterator m(g->second.members.begin());m!=g->second.members.end();++m) { + if (m->address == member) { + g->second.members.erase(m); + break; + } + } + } +} + unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Packet &appendTo,unsigned int limit) const { unsigned char *p; diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index c6c93b1f..0dd199f9 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -106,6 +106,15 @@ public: */ void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown); + /** + * Remove a multicast group member (if present) + * + * @param nwid Network ID + * @param mg Multicast group + * @param member Member to unsubscribe + */ + void remove(uint64_t nwid,const MulticastGroup &mg,const Address &member); + /** * Append gather results to a packet by choosing registered multicast recipients at random * -- cgit v1.2.3