diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..7411a0c6a --- /dev/null +++ b/.clang-format @@ -0,0 +1,75 @@ +--- +BasedOnStyle: LLVM +BreakBeforeBraces: Stroustrup +IndentWidth: 4 +TabWidth: 4 +AlignAfterOpenBracket: AlwaysBreak +AlignConsecutiveMacros: 'true' +AlignConsecutiveAssignments: 'false' +AlignConsecutiveDeclarations: 'false' +AlignEscapedNewlines: Right +AlignOperands: 'true' +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'false' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AllowShortBlocksOnASingleLine: 'true' +AllowShortCaseLabelsOnASingleLine: 'false' +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AlwaysBreakAfterReturnType: None +BinPackArguments: 'false' +BinPackParameters: 'false' +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeTernaryOperators: 'true' +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +CompactNamespaces: 'false' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' +ConstructorInitializerIndentWidth: '4' +ContinuationIndentWidth: '4' +Cpp11BracedListStyle: 'false' +FixNamespaceComments: 'true' +IncludeBlocks: Regroup +IndentCaseLabels: 'true' +IndentPPDirectives: None +IndentWrappedFunctionNames: 'false' +KeepEmptyLinesAtTheStartOfBlocks: 'false' +MaxEmptyLinesToKeep: '1' +NamespaceIndentation: None +PointerAlignment: Left +ReflowComments: 'true' +SortIncludes: 'true' +SortUsingDeclarations: 'true' +SpaceAfterCStyleCast: 'false' +SpaceAfterLogicalNot: 'true' +SpaceAfterTemplateKeyword: 'true' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeCpp11BracedList: 'true' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyParentheses: 'false' +SpacesBeforeTrailingComments: '3' +SpacesInAngles: 'false' +SpacesInCStyleCastParentheses: 'false' +SpacesInContainerLiterals: 'true' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +UseTab: 'Never' + +--- +Language: Cpp +Standard: Cpp03 +ColumnLimit: '240' +--- +Language: ObjC +ColumnLimit: '240' +--- +Language: Java +ColumnLimit: '240' +--- +Language: CSharp +ColumnLimit: '240' +... diff --git a/root/root.cpp b/root/root.cpp index b67ed3548..f5b45b089 100644 --- a/root/root.cpp +++ b/root/root.cpp @@ -48,72 +48,65 @@ * they appear with the first alive sibling being used. */ - +#include "../ext/cpp-httplib/httplib.h" +#include "../ext/json/json.hpp" +#include "../node/Address.hpp" +#include "../node/CertificateOfMembership.hpp" #include "../node/Constants.hpp" +#include "../node/Identity.hpp" +#include "../node/InetAddress.hpp" +#include "../node/Meter.hpp" +#include "../node/MulticastGroup.hpp" +#include "../node/Mutex.hpp" +#include "../node/Packet.hpp" +#include "../node/SharedPtr.hpp" +#include "../node/Utils.hpp" +#include "../osdep/BlockingQueue.hpp" +#include "../osdep/OSUtils.hpp" +#include "geoip-html.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include - -#include "../ext/json/json.hpp" -#include "../ext/cpp-httplib/httplib.h" - -#include "../node/Packet.hpp" -#include "../node/Utils.hpp" -#include "../node/Address.hpp" -#include "../node/Identity.hpp" -#include "../node/InetAddress.hpp" -#include "../node/Mutex.hpp" -#include "../node/SharedPtr.hpp" -#include "../node/MulticastGroup.hpp" -#include "../node/CertificateOfMembership.hpp" -#include "../node/Meter.hpp" - -#include "../osdep/OSUtils.hpp" -#include "../osdep/BlockingQueue.hpp" - -#include -#include -#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include - -#include "geoip-html.h" using namespace ZeroTier; using json = nlohmann::json; #ifdef MSG_DONTWAIT -#define SENDTO_FLAGS MSG_DONTWAIT +#define SENDTO_FLAGS MSG_DONTWAIT #define RECVFROM_FLAGS 0 #else -#define SENDTO_FLAGS 0 +#define SENDTO_FLAGS 0 #define RECVFROM_FLAGS 0 #endif @@ -127,74 +120,110 @@ using json = nlohmann::json; * AtomicCounter all satisfy this. Take care when adding fields that * this remains true. */ -struct RootPeer -{ - ZT_ALWAYS_INLINE RootPeer() : v4s(-1),v6s(-1),lastSend(0),lastReceive(0),lastReceiveV4(0),lastReceiveV6(0),lastEcho(0),lastHello(0),vProto(-1),vMajor(-1),vMinor(-1),vRev(-1),identityValidated(false) {} - ZT_ALWAYS_INLINE ~RootPeer() { Utils::burn(key,sizeof(key)); } +struct RootPeer { + ZT_ALWAYS_INLINE RootPeer() : v4s(-1), v6s(-1), lastSend(0), lastReceive(0), lastReceiveV4(0), lastReceiveV6(0), lastEcho(0), lastHello(0), vProto(-1), vMajor(-1), vMinor(-1), vRev(-1), identityValidated(false) + { + } + ZT_ALWAYS_INLINE ~RootPeer() + { + Utils::burn(key, sizeof(key)); + } - Identity id; // Identity - uint8_t key[32]; // Shared secret key - InetAddress ip4,ip6; // IPv4 and IPv6 addresses - int v4s, v6s; // IPv4 and IPv6 sockets - int64_t lastSend; // Time of last send (any packet) - int64_t lastReceive; // Time of last receive (any packet) - int64_t lastReceiveV4; // Time of last IPv4 receive - int64_t lastReceiveV6; // Time of last IPv6 receive - int64_t lastEcho; // Time of last received ECHO - int64_t lastHello; // Time of last received HELLO - int vProto; // Protocol version or -1 if unknown - int vMajor,vMinor,vRev; // Peer version or -1,-1,-1 if unknown - bool identityValidated; // Identity has been fully verified + Identity id; // Identity + uint8_t key[32]; // Shared secret key + InetAddress ip4, ip6; // IPv4 and IPv6 addresses + int v4s, v6s; // IPv4 and IPv6 sockets + int64_t lastSend; // Time of last send (any packet) + int64_t lastReceive; // Time of last receive (any packet) + int64_t lastReceiveV4; // Time of last IPv4 receive + int64_t lastReceiveV6; // Time of last IPv6 receive + int64_t lastEcho; // Time of last received ECHO + int64_t lastHello; // Time of last received HELLO + int vProto; // Protocol version or -1 if unknown + int vMajor, vMinor, vRev; // Peer version or -1,-1,-1 if unknown + bool identityValidated; // Identity has been fully verified - AtomicCounter __refCount; + AtomicCounter __refCount; }; // Hashers for std::unordered_map -struct IdentityHasher { ZT_ALWAYS_INLINE std::size_t operator()(const Identity &id) const { return (std::size_t)id.hashCode(); } }; -struct AddressHasher { ZT_ALWAYS_INLINE std::size_t operator()(const Address &a) const { return (std::size_t)a.toInt(); } }; -struct InetAddressHasher { ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress &ip) const { return (std::size_t)ip.hashCode(); } }; -struct MulticastGroupHasher { ZT_ALWAYS_INLINE std::size_t operator()(const MulticastGroup &mg) const { return (std::size_t)mg.hashCode(); } }; - -// An ordered tuple key representing an introduction of one peer to another -struct RendezvousKey -{ - RendezvousKey(const Address &aa,const Address &bb) - { - if (aa > bb) { - a = aa; - b = bb; - } else { - a = bb; - b = aa; - } - } - Address a,b; - ZT_ALWAYS_INLINE bool operator==(const RendezvousKey &k) const { return ((a == k.a)&&(b == k.b)); } - ZT_ALWAYS_INLINE bool operator!=(const RendezvousKey &k) const { return ((a != k.a)||(b != k.b)); } - struct Hasher { ZT_ALWAYS_INLINE std::size_t operator()(const RendezvousKey &k) const { return (std::size_t)(k.a.toInt() ^ k.b.toInt()); } }; +struct IdentityHasher { + ZT_ALWAYS_INLINE std::size_t operator()(const Identity& id) const + { + return (std::size_t)id.hashCode(); + } +}; +struct AddressHasher { + ZT_ALWAYS_INLINE std::size_t operator()(const Address& a) const + { + return (std::size_t)a.toInt(); + } +}; +struct InetAddressHasher { + ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress& ip) const + { + return (std::size_t)ip.hashCode(); + } +}; +struct MulticastGroupHasher { + ZT_ALWAYS_INLINE std::size_t operator()(const MulticastGroup& mg) const + { + return (std::size_t)mg.hashCode(); + } }; -struct RendezvousStats -{ - RendezvousStats() : count(0),ts(0) {} - int64_t count; - int64_t ts; +// An ordered tuple key representing an introduction of one peer to another +struct RendezvousKey { + RendezvousKey(const Address& aa, const Address& bb) + { + if (aa > bb) { + a = aa; + b = bb; + } + else { + a = bb; + b = aa; + } + } + Address a, b; + ZT_ALWAYS_INLINE bool operator==(const RendezvousKey& k) const + { + return ((a == k.a) && (b == k.b)); + } + ZT_ALWAYS_INLINE bool operator!=(const RendezvousKey& k) const + { + return ((a != k.a) || (b != k.b)); + } + struct Hasher { + ZT_ALWAYS_INLINE std::size_t operator()(const RendezvousKey& k) const + { + return (std::size_t)(k.a.toInt() ^ k.b.toInt()); + } + }; +}; + +struct RendezvousStats { + RendezvousStats() : count(0), ts(0) + { + } + int64_t count; + int64_t ts; }; // These fields are not locked as they're only initialized on startup or are atomic -static int64_t s_startTime; // Time service was started -static std::vector s_ports; // Ports to bind for UDP traffic -static int s_relayMaxHops = 0; // Max relay hops -static Identity s_self; // My identity (including secret) -static std::atomic_bool s_run; // Remains true until shutdown is ordered -static json s_config; // JSON config file contents -static std::string s_statsRoot; // Root to write stats, peers, etc. -static std::atomic_bool s_geoInit; // True if geoIP data is initialized -static std::string s_googleMapsAPIKey; // Google maps API key for GeoIP /map feature +static int64_t s_startTime; // Time service was started +static std::vector s_ports; // Ports to bind for UDP traffic +static int s_relayMaxHops = 0; // Max relay hops +static Identity s_self; // My identity (including secret) +static std::atomic_bool s_run; // Remains true until shutdown is ordered +static json s_config; // JSON config file contents +static std::string s_statsRoot; // Root to write stats, peers, etc. +static std::atomic_bool s_geoInit; // True if geoIP data is initialized +static std::string s_googleMapsAPIKey; // Google maps API key for GeoIP /map feature // These are only modified during GeoIP database load (if enabled) and become static after s_geoInit is set to true. -static std::map< std::pair< uint32_t,uint32_t >,std::pair< float,float > > s_geoIp4; -static std::map< std::pair< std::array< uint64_t,2 >,std::array< uint64_t,2 > >,std::pair< float,float > > s_geoIp6; +static std::map, std::pair > s_geoIp4; +static std::map, std::array >, std::pair > s_geoIp6; // Rate meters for statistical purposes (locks are internal to Meter) static Meter s_inputRate; @@ -204,11 +233,11 @@ static Meter s_discardedForwardRate; // These fields are locked using mutexes below as they're modified during runtime static std::string s_planet; -static std::vector< SharedPtr > s_peers; -static std::vector< SharedPtr > s_peersToValidate; -static std::unordered_map< uint64_t,std::unordered_map< MulticastGroup,std::unordered_map< Address,int64_t,AddressHasher >,MulticastGroupHasher > > s_multicastSubscriptions; -static std::unordered_map< Address,SharedPtr,AddressHasher > s_peersByVirtAddr; -static std::unordered_map< RendezvousKey,RendezvousStats,RendezvousKey::Hasher > s_rendezvousTracking; +static std::vector > s_peers; +static std::vector > s_peersToValidate; +static std::unordered_map, MulticastGroupHasher> > s_multicastSubscriptions; +static std::unordered_map, AddressHasher> s_peersByVirtAddr; +static std::unordered_map s_rendezvousTracking; static std::mutex s_planet_l; static std::mutex s_peers_l; @@ -221,1166 +250,1225 @@ static std::mutex s_rendezvousTracking_l; ////////////////////////////////////////////////////////////////////////////// // Construct GeoIP key for IPv4 IPs -static ZT_ALWAYS_INLINE uint32_t ip4ToH32(const InetAddress &ip) +static ZT_ALWAYS_INLINE uint32_t ip4ToH32(const InetAddress& ip) { - return Utils::ntoh((uint32_t)(((const struct sockaddr_in *)&ip)->sin_addr.s_addr)); + return Utils::ntoh((uint32_t)(((const struct sockaddr_in*)&ip)->sin_addr.s_addr)); } // Construct GeoIP key for IPv6 IPs -static ZT_ALWAYS_INLINE std::array< uint64_t,2 > ip6ToH128(const InetAddress &ip) +static ZT_ALWAYS_INLINE std::array ip6ToH128(const InetAddress& ip) { - std::array i128; - memcpy(i128.data(),ip.rawIpData(),16); - i128[0] = Utils::ntoh(i128[0]); - i128[1] = Utils::ntoh(i128[1]); - return i128; + std::array i128; + memcpy(i128.data(), ip.rawIpData(), 16); + i128[0] = Utils::ntoh(i128[0]); + i128[1] = Utils::ntoh(i128[1]); + return i128; } -static void handlePacket(const int sock,const InetAddress *const ip,Packet &pkt) +static void handlePacket(const int sock, const InetAddress* const ip, Packet& pkt) { - char ipstr[128],ipstr2[128],astr[32],astr2[32],tmpstr[256]; - const bool fragment = pkt[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR; - const Address source(pkt.source()); - const Address dest(pkt.destination()); - const int64_t now = OSUtils::now(); + char ipstr[128], ipstr2[128], astr[32], astr2[32], tmpstr[256]; + const bool fragment = pkt[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR; + const Address source(pkt.source()); + const Address dest(pkt.destination()); + const int64_t now = OSUtils::now(); - s_inputRate.log(now,pkt.size()); + s_inputRate.log(now, pkt.size()); - if ((!fragment)&&(pkt.size() < ZT_PROTO_MIN_PACKET_LENGTH)) - return; + if ((! fragment) && (pkt.size() < ZT_PROTO_MIN_PACKET_LENGTH)) + return; - if ((!fragment)&&(!pkt.fragmented())&&(dest == s_self.address())) { - SharedPtr peer; + if ((! fragment) && (! pkt.fragmented()) && (dest == s_self.address())) { + SharedPtr peer; - // If this is an un-encrypted HELLO, either learn a new peer or verify - // that this is a peer we already know. - if ((pkt.cipher() == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE)&&(pkt.verb() == Packet::VERB_HELLO)) { - Identity id; - if (id.deserialize(pkt,ZT_PROTO_VERB_HELLO_IDX_IDENTITY)) { - { - std::lock_guard p_l(s_peersByVirtAddr_l); - auto p = s_peersByVirtAddr.find(source); - if (p != s_peersByVirtAddr.end()) { - peer = p->second; - } - } + // If this is an un-encrypted HELLO, either learn a new peer or verify + // that this is a peer we already know. + if ((pkt.cipher() == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE) && (pkt.verb() == Packet::VERB_HELLO)) { + Identity id; + if (id.deserialize(pkt, ZT_PROTO_VERB_HELLO_IDX_IDENTITY)) { + { + std::lock_guard p_l(s_peersByVirtAddr_l); + auto p = s_peersByVirtAddr.find(source); + if (p != s_peersByVirtAddr.end()) { + peer = p->second; + } + } - if (peer) { - if (unlikely(peer->id != id)) { - printf("%s HELLO rejected: identity address collision!" ZT_EOL_S,ip->toString(ipstr)); + if (peer) { + if (unlikely(peer->id != id)) { + printf("%s HELLO rejected: identity address collision!" ZT_EOL_S, ip->toString(ipstr)); - uint8_t key[48]; - if (s_self.agree(id, key)) { - const uint64_t origId = pkt.packetId(); - pkt.reset(source,s_self.address(),Packet::VERB_ERROR); - pkt.append((uint8_t)Packet::VERB_HELLO); - pkt.append(origId);; - pkt.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION); - pkt.armor(key,true); - sendto(sock,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)ip,(socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); - } + uint8_t key[48]; + if (s_self.agree(id, key)) { + const uint64_t origId = pkt.packetId(); + pkt.reset(source, s_self.address(), Packet::VERB_ERROR); + pkt.append((uint8_t)Packet::VERB_HELLO); + pkt.append(origId); + ; + pkt.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION); + pkt.armor(key, true); + sendto(sock, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)ip, (socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); + } - return; - } - } else { - peer.set(new RootPeer); - peer->identityValidated = false; + return; + } + } + else { + peer.set(new RootPeer); + peer->identityValidated = false; - if (!s_self.agree(id,peer->key)) { - printf("%s HELLO rejected: key agreement failed" ZT_EOL_S,ip->toString(ipstr)); - return; - } - if (!pkt.dearmor(peer->key)) { - printf("%s HELLO rejected: packet authentication failed" ZT_EOL_S,ip->toString(ipstr)); - return; - } - if (!pkt.uncompress()) { - printf("%s HELLO rejected: decompression failed" ZT_EOL_S,ip->toString(ipstr)); - return; - } + if (! s_self.agree(id, peer->key)) { + printf("%s HELLO rejected: key agreement failed" ZT_EOL_S, ip->toString(ipstr)); + return; + } + if (! pkt.dearmor(peer->key)) { + printf("%s HELLO rejected: packet authentication failed" ZT_EOL_S, ip->toString(ipstr)); + return; + } + if (! pkt.uncompress()) { + printf("%s HELLO rejected: decompression failed" ZT_EOL_S, ip->toString(ipstr)); + return; + } - peer->id = id; - peer->lastReceive = now; + peer->id = id; + peer->lastReceive = now; - { - std::lock_guard pbv_l(s_peersByVirtAddr_l); - s_peersByVirtAddr[id.address()] = peer; - } - { - std::lock_guard pl(s_peers_l); - s_peers.emplace_back(peer); - } - { - std::lock_guard pv(s_peersToValidate_l); - s_peersToValidate.emplace_back(peer); - } - } - } - } + { + std::lock_guard pbv_l(s_peersByVirtAddr_l); + s_peersByVirtAddr[id.address()] = peer; + } + { + std::lock_guard pl(s_peers_l); + s_peers.emplace_back(peer); + } + { + std::lock_guard pv(s_peersToValidate_l); + s_peersToValidate.emplace_back(peer); + } + } + } + } - if (!peer) { - { - std::lock_guard pbv_l(s_peersByVirtAddr_l); - auto p = s_peersByVirtAddr.find(source); - if (p != s_peersByVirtAddr.end()) { - peer = p->second; - } - } - if (peer) { - if (!pkt.dearmor(peer->key)) { - printf("%s HELLO rejected: packet authentication failed" ZT_EOL_S,ip->toString(ipstr)); - return; - } - if (!pkt.uncompress()) { - printf("%s packet rejected: decompression failed" ZT_EOL_S,ip->toString(ipstr)); - return; - } - } else { - return; - } - } + if (! peer) { + { + std::lock_guard pbv_l(s_peersByVirtAddr_l); + auto p = s_peersByVirtAddr.find(source); + if (p != s_peersByVirtAddr.end()) { + peer = p->second; + } + } + if (peer) { + if (! pkt.dearmor(peer->key)) { + printf("%s HELLO rejected: packet authentication failed" ZT_EOL_S, ip->toString(ipstr)); + return; + } + if (! pkt.uncompress()) { + printf("%s packet rejected: decompression failed" ZT_EOL_S, ip->toString(ipstr)); + return; + } + } + else { + return; + } + } - const int64_t now = OSUtils::now(); + const int64_t now = OSUtils::now(); - if (ip->isV4()) { - peer->ip4 = ip; - peer->v4s = sock; - peer->lastReceiveV4 = now; - if ((now - peer->lastReceiveV6) > ZT_PEER_ACTIVITY_TIMEOUT) - peer->v6s = -1; - } else if (ip->isV6()) { - peer->ip6 = ip; - peer->v6s = sock; - peer->lastReceiveV6 = now; - if ((now - peer->lastReceiveV4) > ZT_PEER_ACTIVITY_TIMEOUT) - peer->v4s = -1; - } - peer->lastReceive = now; + if (ip->isV4()) { + peer->ip4 = ip; + peer->v4s = sock; + peer->lastReceiveV4 = now; + if ((now - peer->lastReceiveV6) > ZT_PEER_ACTIVITY_TIMEOUT) + peer->v6s = -1; + } + else if (ip->isV6()) { + peer->ip6 = ip; + peer->v6s = sock; + peer->lastReceiveV6 = now; + if ((now - peer->lastReceiveV4) > ZT_PEER_ACTIVITY_TIMEOUT) + peer->v4s = -1; + } + peer->lastReceive = now; - switch(pkt.verb()) { - case Packet::VERB_HELLO: - try { - if ((now - peer->lastHello) > 250) { - peer->lastHello = now; + switch (pkt.verb()) { + case Packet::VERB_HELLO: + try { + if ((now - peer->lastHello) > 250) { + peer->lastHello = now; - peer->vProto = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; - peer->vMajor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; - peer->vMinor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; - peer->vRev = (int)pkt.template at(ZT_PROTO_VERB_HELLO_IDX_REVISION); - const uint64_t origId = pkt.packetId(); - const uint64_t ts = pkt.template at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); + peer->vProto = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; + peer->vMajor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; + peer->vMinor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; + peer->vRev = (int)pkt.template at(ZT_PROTO_VERB_HELLO_IDX_REVISION); + const uint64_t origId = pkt.packetId(); + const uint64_t ts = pkt.template at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); - pkt.reset(source,s_self.address(),Packet::VERB_OK); - pkt.append((uint8_t)Packet::VERB_HELLO); - pkt.append(origId); - pkt.append(ts); - pkt.append((uint8_t)ZT_PROTO_VERSION); - pkt.append((uint8_t)0); - pkt.append((uint8_t)0); - pkt.append((uint16_t)0); - ip->serialize(pkt); - if (peer->vProto < 20) { // send planet file for pre-2.x peers - std::lock_guard pl(s_planet_l); - if (s_planet.length() > 0) { - pkt.append((uint16_t)s_planet.size()); - pkt.append((const uint8_t *)s_planet.data(),s_planet.size()); - } - } - pkt.armor(peer->key,true); - sendto(sock,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)ip,(socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); + pkt.reset(source, s_self.address(), Packet::VERB_OK); + pkt.append((uint8_t)Packet::VERB_HELLO); + pkt.append(origId); + pkt.append(ts); + pkt.append((uint8_t)ZT_PROTO_VERSION); + pkt.append((uint8_t)0); + pkt.append((uint8_t)0); + pkt.append((uint16_t)0); + ip->serialize(pkt); + if (peer->vProto < 20) { // send planet file for pre-2.x peers + std::lock_guard pl(s_planet_l); + if (s_planet.length() > 0) { + pkt.append((uint16_t)s_planet.size()); + pkt.append((const uint8_t*)s_planet.data(), s_planet.size()); + } + } + pkt.armor(peer->key, true); + sendto(sock, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)ip, (socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); - s_outputRate.log(now,pkt.size()); - peer->lastSend = now; - } - } catch ( ... ) { - printf("* unexpected exception handling HELLO from %s" ZT_EOL_S,ip->toString(ipstr)); - } - break; + s_outputRate.log(now, pkt.size()); + peer->lastSend = now; + } + } + catch (...) { + printf("* unexpected exception handling HELLO from %s" ZT_EOL_S, ip->toString(ipstr)); + } + break; - case Packet::VERB_ECHO: - try { - if ((now - peer->lastEcho) > 500) { - peer->lastEcho = now; + case Packet::VERB_ECHO: + try { + if ((now - peer->lastEcho) > 500) { + peer->lastEcho = now; - Packet outp(source,s_self.address(),Packet::VERB_OK); - outp.append((uint8_t)Packet::VERB_ECHO); - outp.append(pkt.packetId()); - outp.append(((const uint8_t *)pkt.data()) + ZT_PACKET_IDX_PAYLOAD,pkt.size() - ZT_PACKET_IDX_PAYLOAD); - outp.compress(); - outp.armor(peer->key,true); - sendto(sock,outp.data(),outp.size(),SENDTO_FLAGS,(const struct sockaddr *)ip,(socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); + Packet outp(source, s_self.address(), Packet::VERB_OK); + outp.append((uint8_t)Packet::VERB_ECHO); + outp.append(pkt.packetId()); + outp.append(((const uint8_t*)pkt.data()) + ZT_PACKET_IDX_PAYLOAD, pkt.size() - ZT_PACKET_IDX_PAYLOAD); + outp.compress(); + outp.armor(peer->key, true); + sendto(sock, outp.data(), outp.size(), SENDTO_FLAGS, (const struct sockaddr*)ip, (socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); - s_outputRate.log(now,outp.size()); - peer->lastSend = now; - } - } catch ( ... ) { - printf("* unexpected exception handling ECHO from %s" ZT_EOL_S,ip->toString(ipstr)); - } + s_outputRate.log(now, outp.size()); + peer->lastSend = now; + } + } + catch (...) { + printf("* unexpected exception handling ECHO from %s" ZT_EOL_S, ip->toString(ipstr)); + } - case Packet::VERB_WHOIS: - try { - std::vector< SharedPtr > results; - results.reserve(4); - { - std::lock_guard l(s_peersByVirtAddr_l); - for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;(ptr+ZT_ADDRESS_LENGTH)<=pkt.size();ptr+=ZT_ADDRESS_LENGTH) { - auto p = s_peersByVirtAddr.find(Address(pkt.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH)); - if (p != s_peersByVirtAddr.end()) { - results.push_back(p->second); - } - } - } + case Packet::VERB_WHOIS: + try { + std::vector > results; + results.reserve(4); + { + std::lock_guard l(s_peersByVirtAddr_l); + for (unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; (ptr + ZT_ADDRESS_LENGTH) <= pkt.size(); ptr += ZT_ADDRESS_LENGTH) { + auto p = s_peersByVirtAddr.find(Address(pkt.field(ptr, ZT_ADDRESS_LENGTH), ZT_ADDRESS_LENGTH)); + if (p != s_peersByVirtAddr.end()) { + results.push_back(p->second); + } + } + } - if (!results.empty()) { - const uint64_t origId = pkt.packetId(); - pkt.reset(source,s_self.address(),Packet::VERB_OK); - pkt.append((uint8_t)Packet::VERB_WHOIS); - pkt.append(origId); - for(auto p=results.begin();p!=results.end();++p) - (*p)->id.serialize(pkt,false); - pkt.armor(peer->key,true); - sendto(sock,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)ip,(socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); + if (! results.empty()) { + const uint64_t origId = pkt.packetId(); + pkt.reset(source, s_self.address(), Packet::VERB_OK); + pkt.append((uint8_t)Packet::VERB_WHOIS); + pkt.append(origId); + for (auto p = results.begin(); p != results.end(); ++p) + (*p)->id.serialize(pkt, false); + pkt.armor(peer->key, true); + sendto(sock, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)ip, (socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); - s_outputRate.log(now,pkt.size()); - peer->lastSend = now; - } - } catch ( ... ) { - printf("* unexpected exception handling ECHO from %s" ZT_EOL_S,ip->toString(ipstr)); - } + s_outputRate.log(now, pkt.size()); + peer->lastSend = now; + } + } + catch (...) { + printf("* unexpected exception handling ECHO from %s" ZT_EOL_S, ip->toString(ipstr)); + } - case Packet::VERB_MULTICAST_LIKE: - try { - std::lock_guard l(s_multicastSubscriptions_l); - for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;(ptr+18)<=pkt.size();ptr+=18) { - const uint64_t nwid = pkt.template at(ptr); - const MulticastGroup mg(MAC(pkt.field(ptr + 8,6),6),pkt.template at(ptr + 14)); - s_multicastSubscriptions[nwid][mg][source] = now; - } - } catch ( ... ) { - printf("* unexpected exception handling MULTICAST_LIKE from %s" ZT_EOL_S,ip->toString(ipstr)); - } - break; + case Packet::VERB_MULTICAST_LIKE: + try { + std::lock_guard l(s_multicastSubscriptions_l); + for (unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; (ptr + 18) <= pkt.size(); ptr += 18) { + const uint64_t nwid = pkt.template at(ptr); + const MulticastGroup mg(MAC(pkt.field(ptr + 8, 6), 6), pkt.template at(ptr + 14)); + s_multicastSubscriptions[nwid][mg][source] = now; + } + } + catch (...) { + printf("* unexpected exception handling MULTICAST_LIKE from %s" ZT_EOL_S, ip->toString(ipstr)); + } + break; - case Packet::VERB_MULTICAST_GATHER: - try { - const uint64_t nwid = pkt.template at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID); - //const unsigned int flags = pkt[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS]; - const MulticastGroup mg(MAC(pkt.field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),pkt.template at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI)); - unsigned int gatherLimit = pkt.template at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT); - if (gatherLimit > 255) - gatherLimit = 255; + case Packet::VERB_MULTICAST_GATHER: + try { + const uint64_t nwid = pkt.template at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID); + // const unsigned int flags = pkt[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS]; + const MulticastGroup mg(MAC(pkt.field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC, 6), 6), pkt.template at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI)); + unsigned int gatherLimit = pkt.template at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT); + if (gatherLimit > 255) + gatherLimit = 255; - const uint64_t origId = pkt.packetId(); - pkt.reset(source,s_self.address(),Packet::VERB_OK); - pkt.append((uint8_t)Packet::VERB_MULTICAST_GATHER); - pkt.append(origId); - pkt.append(nwid); - mg.mac().appendTo(pkt); - pkt.append((uint32_t)mg.adi()); + const uint64_t origId = pkt.packetId(); + pkt.reset(source, s_self.address(), Packet::VERB_OK); + pkt.append((uint8_t)Packet::VERB_MULTICAST_GATHER); + pkt.append(origId); + pkt.append(nwid); + mg.mac().appendTo(pkt); + pkt.append((uint32_t)mg.adi()); - { - std::lock_guard l(s_multicastSubscriptions_l); - auto forNet = s_multicastSubscriptions.find(nwid); - if (forNet != s_multicastSubscriptions.end()) { - auto forGroup = forNet->second.find(mg); - if (forGroup != forNet->second.end()) { - pkt.append((uint32_t)forGroup->second.size()); - const unsigned int countAt = pkt.size(); - pkt.addSize(2); + { + std::lock_guard l(s_multicastSubscriptions_l); + auto forNet = s_multicastSubscriptions.find(nwid); + if (forNet != s_multicastSubscriptions.end()) { + auto forGroup = forNet->second.find(mg); + if (forGroup != forNet->second.end()) { + pkt.append((uint32_t)forGroup->second.size()); + const unsigned int countAt = pkt.size(); + pkt.addSize(2); - unsigned int l = 0; - for(auto g=forGroup->second.begin();((lsecond.end()));++g) { - if (g->first != source) { - ++l; - g->first.appendTo(pkt); - } - } + unsigned int l = 0; + for (auto g = forGroup->second.begin(); ((l < gatherLimit) && (g != forGroup->second.end())); ++g) { + if (g->first != source) { + ++l; + g->first.appendTo(pkt); + } + } - if (l > 0) { - pkt.setAt(countAt,(uint16_t)l); - pkt.armor(peer->key,true); - sendto(sock,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)ip,(socklen_t)(ip->isV4() ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); + if (l > 0) { + pkt.setAt(countAt, (uint16_t)l); + pkt.armor(peer->key, true); + sendto(sock, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)ip, (socklen_t)(ip->isV4() ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); - s_outputRate.log(now,pkt.size()); - peer->lastSend = now; - } - } - } - } - } catch ( ... ) { - printf("* unexpected exception handling MULTICAST_GATHER from %s" ZT_EOL_S,ip->toString(ipstr)); - } - break; + s_outputRate.log(now, pkt.size()); + peer->lastSend = now; + } + } + } + } + } + catch (...) { + printf("* unexpected exception handling MULTICAST_GATHER from %s" ZT_EOL_S, ip->toString(ipstr)); + } + break; - default: - break; - } + default: + break; + } - return; - } + return; + } - // If we made it here, we are forwarding this packet to someone else and also possibly - // sending a RENDEZVOUS message. + // If we made it here, we are forwarding this packet to someone else and also possibly + // sending a RENDEZVOUS message. - int hops = 0; - bool introduce = false; - if (fragment) { - if ((hops = (int)reinterpret_cast(&pkt)->incrementHops()) > s_relayMaxHops) { - //printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); - s_discardedForwardRate.log(now,pkt.size()); - return; - } - } else { - if ((hops = (int)pkt.incrementHops()) > s_relayMaxHops) { - //printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); - s_discardedForwardRate.log(now,pkt.size()); - return; - } + int hops = 0; + bool introduce = false; + if (fragment) { + if ((hops = (int)reinterpret_cast(&pkt)->incrementHops()) > s_relayMaxHops) { + // printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); + s_discardedForwardRate.log(now, pkt.size()); + return; + } + } + else { + if ((hops = (int)pkt.incrementHops()) > s_relayMaxHops) { + // printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); + s_discardedForwardRate.log(now, pkt.size()); + return; + } - if (hops == 1) { - RendezvousKey rk(source,dest); - std::lock_guard l(s_rendezvousTracking_l); - RendezvousStats &lr = s_rendezvousTracking[rk]; - if ((now - lr.ts) >= 30000) { - ++lr.count; - lr.ts = now; - introduce = true; - } - } - } + if (hops == 1) { + RendezvousKey rk(source, dest); + std::lock_guard l(s_rendezvousTracking_l); + RendezvousStats& lr = s_rendezvousTracking[rk]; + if ((now - lr.ts) >= 30000) { + ++lr.count; + lr.ts = now; + introduce = true; + } + } + } - SharedPtr forwardTo; - { - std::lock_guard pbv_l(s_peersByVirtAddr_l); - auto p = s_peersByVirtAddr.find(dest); - if (p != s_peersByVirtAddr.end()) { - forwardTo = p->second; - } - } + SharedPtr forwardTo; + { + std::lock_guard pbv_l(s_peersByVirtAddr_l); + auto p = s_peersByVirtAddr.find(dest); + if (p != s_peersByVirtAddr.end()) { + forwardTo = p->second; + } + } - if (unlikely(!forwardTo)) { - s_discardedForwardRate.log(now,pkt.size()); - return; - } + if (unlikely(! forwardTo)) { + s_discardedForwardRate.log(now, pkt.size()); + return; + } - if (introduce) { - SharedPtr sourcePeer; - { - std::lock_guard l(s_peersByVirtAddr_l); - auto sp = s_peersByVirtAddr.find(source); - if (sp != s_peersByVirtAddr.end()) { - sourcePeer = sp->second; - } - } - if (likely(sourcePeer)) { - if ((sourcePeer->v6s >= 0)&&(forwardTo->v6s >= 0)) { - Packet outp(dest,s_self.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - source.appendTo(outp); - outp.append((uint16_t)sourcePeer->ip6.port()); - outp.append((uint8_t)16); - outp.append((const uint8_t *)(sourcePeer->ip6.rawIpData()),16); - outp.armor(forwardTo->key,true); - sendto(forwardTo->v6s,outp.data(),outp.size(),SENDTO_FLAGS,(const struct sockaddr *)&(forwardTo->ip6),(socklen_t)sizeof(struct sockaddr_in6)); + if (introduce) { + SharedPtr sourcePeer; + { + std::lock_guard l(s_peersByVirtAddr_l); + auto sp = s_peersByVirtAddr.find(source); + if (sp != s_peersByVirtAddr.end()) { + sourcePeer = sp->second; + } + } + if (likely(sourcePeer)) { + if ((sourcePeer->v6s >= 0) && (forwardTo->v6s >= 0)) { + Packet outp(dest, s_self.address(), Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); + source.appendTo(outp); + outp.append((uint16_t)sourcePeer->ip6.port()); + outp.append((uint8_t)16); + outp.append((const uint8_t*)(sourcePeer->ip6.rawIpData()), 16); + outp.armor(forwardTo->key, true); + sendto(forwardTo->v6s, outp.data(), outp.size(), SENDTO_FLAGS, (const struct sockaddr*)&(forwardTo->ip6), (socklen_t)sizeof(struct sockaddr_in6)); - s_outputRate.log(now,outp.size()); - forwardTo->lastSend = now; + s_outputRate.log(now, outp.size()); + forwardTo->lastSend = now; - outp.reset(source,s_self.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - dest.appendTo(outp); - outp.append((uint16_t)forwardTo->ip6.port()); - outp.append((uint8_t)16); - outp.append((const uint8_t *)(forwardTo->ip6.rawIpData()),16); - outp.armor(sourcePeer->key,true); - sendto(sourcePeer->v6s,outp.data(),outp.size(),SENDTO_FLAGS,(const struct sockaddr *)&(sourcePeer->ip6),(socklen_t)sizeof(struct sockaddr_in6)); + outp.reset(source, s_self.address(), Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); + dest.appendTo(outp); + outp.append((uint16_t)forwardTo->ip6.port()); + outp.append((uint8_t)16); + outp.append((const uint8_t*)(forwardTo->ip6.rawIpData()), 16); + outp.armor(sourcePeer->key, true); + sendto(sourcePeer->v6s, outp.data(), outp.size(), SENDTO_FLAGS, (const struct sockaddr*)&(sourcePeer->ip6), (socklen_t)sizeof(struct sockaddr_in6)); - s_outputRate.log(now,outp.size()); - sourcePeer->lastSend = now; - } + s_outputRate.log(now, outp.size()); + sourcePeer->lastSend = now; + } - if ((sourcePeer->v4s >= 0)&&(forwardTo->v4s >= 0)) { - Packet outp(dest,s_self.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - source.appendTo(outp); - outp.append((uint16_t)sourcePeer->ip4.port()); - outp.append((uint8_t)4); - outp.append((const uint8_t *)sourcePeer->ip4.rawIpData(),4); - outp.armor(forwardTo->key,true); - sendto(forwardTo->v4s,outp.data(),outp.size(),SENDTO_FLAGS,(const struct sockaddr *)&(forwardTo->ip4),(socklen_t)sizeof(struct sockaddr_in)); + if ((sourcePeer->v4s >= 0) && (forwardTo->v4s >= 0)) { + Packet outp(dest, s_self.address(), Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); + source.appendTo(outp); + outp.append((uint16_t)sourcePeer->ip4.port()); + outp.append((uint8_t)4); + outp.append((const uint8_t*)sourcePeer->ip4.rawIpData(), 4); + outp.armor(forwardTo->key, true); + sendto(forwardTo->v4s, outp.data(), outp.size(), SENDTO_FLAGS, (const struct sockaddr*)&(forwardTo->ip4), (socklen_t)sizeof(struct sockaddr_in)); - s_outputRate.log(now,outp.size()); - forwardTo->lastSend = now; + s_outputRate.log(now, outp.size()); + forwardTo->lastSend = now; - outp.reset(source,s_self.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - dest.appendTo(outp); - outp.append((uint16_t)forwardTo->ip4.port()); - outp.append((uint8_t)4); - outp.append((const uint8_t *)(forwardTo->ip4.rawIpData()),4); - outp.armor(sourcePeer->key,true); - sendto(sourcePeer->v4s,outp.data(),outp.size(),SENDTO_FLAGS,(const struct sockaddr *)&(sourcePeer->ip4),(socklen_t)sizeof(struct sockaddr_in)); + outp.reset(source, s_self.address(), Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); + dest.appendTo(outp); + outp.append((uint16_t)forwardTo->ip4.port()); + outp.append((uint8_t)4); + outp.append((const uint8_t*)(forwardTo->ip4.rawIpData()), 4); + outp.armor(sourcePeer->key, true); + sendto(sourcePeer->v4s, outp.data(), outp.size(), SENDTO_FLAGS, (const struct sockaddr*)&(sourcePeer->ip4), (socklen_t)sizeof(struct sockaddr_in)); - s_outputRate.log(now,outp.size()); - sourcePeer->lastSend = now; - } - } - } + s_outputRate.log(now, outp.size()); + sourcePeer->lastSend = now; + } + } + } - if (forwardTo->v6s >= 0) { - if (sendto(forwardTo->v6s,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)&(forwardTo->ip6),(socklen_t)sizeof(struct sockaddr_in6)) > 0) { - s_outputRate.log(now,pkt.size()); - s_forwardRate.log(now,pkt.size()); - forwardTo->lastSend = now; - } - } else if (forwardTo->v4s >= 0) { - if (sendto(forwardTo->v4s,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)&(forwardTo->ip4),(socklen_t)sizeof(struct sockaddr_in)) > 0) { - s_outputRate.log(now,pkt.size()); - s_forwardRate.log(now,pkt.size()); - forwardTo->lastSend = now; - } - } + if (forwardTo->v6s >= 0) { + if (sendto(forwardTo->v6s, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)&(forwardTo->ip6), (socklen_t)sizeof(struct sockaddr_in6)) > 0) { + s_outputRate.log(now, pkt.size()); + s_forwardRate.log(now, pkt.size()); + forwardTo->lastSend = now; + } + } + else if (forwardTo->v4s >= 0) { + if (sendto(forwardTo->v4s, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)&(forwardTo->ip4), (socklen_t)sizeof(struct sockaddr_in)) > 0) { + s_outputRate.log(now, pkt.size()); + s_forwardRate.log(now, pkt.size()); + forwardTo->lastSend = now; + } + } } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -static int bindSocket(struct sockaddr *const bindAddr) +static int bindSocket(struct sockaddr* const bindAddr) { - const int s = socket(bindAddr->sa_family,SOCK_DGRAM,0); - if (s < 0) { - close(s); - return -1; - } + const int s = socket(bindAddr->sa_family, SOCK_DGRAM, 0); + if (s < 0) { + close(s); + return -1; + } - int f = 16777216; - while (f > 65536) { - if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&f,sizeof(f)) == 0) - break; - f -= 65536; - } - f = 16777216; - while (f > 65536) { - if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&f,sizeof(f)) == 0) - break; - f -= 65536; - } + int f = 16777216; + while (f > 65536) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*)&f, sizeof(f)) == 0) + break; + f -= 65536; + } + f = 16777216; + while (f > 65536) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&f, sizeof(f)) == 0) + break; + f -= 65536; + } - if (bindAddr->sa_family == AF_INET6) { - f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); + if (bindAddr->sa_family == AF_INET6) { + f = 1; + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&f, sizeof(f)); #ifdef IPV6_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); + f = 0; + setsockopt(s, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &f, sizeof(f)); #endif #ifdef IPV6_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,&f,sizeof(f)); + f = 0; + setsockopt(s, IPPROTO_IPV6, IPV6_DONTFRAG, &f, sizeof(f)); #endif - } + } #ifdef IP_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); + f = 0; + setsockopt(s, IPPROTO_IP, IP_DONTFRAG, &f, sizeof(f)); #endif #ifdef IP_MTU_DISCOVER - f = IP_PMTUDISC_DONT; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); + f = IP_PMTUDISC_DONT; + setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &f, sizeof(f)); #endif -/* -#ifdef SO_NO_CHECK - if (bindAddr->sa_family == AF_INET) { - f = 1; setsockopt(s,SOL_SOCKET,SO_NO_CHECK,(void *)&f,sizeof(f)); - } -#endif -*/ + /* + #ifdef SO_NO_CHECK + if (bindAddr->sa_family == AF_INET) { + f = 1; setsockopt(s,SOL_SOCKET,SO_NO_CHECK,(void *)&f,sizeof(f)); + } + #endif + */ #ifdef SO_REUSEPORT - f = 1; setsockopt(s,SOL_SOCKET,SO_REUSEPORT,(void *)&f,sizeof(f)); + f = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&f, sizeof(f)); #endif -#ifndef __LINUX__ // linux wants just SO_REUSEPORT - f = 1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); +#ifndef __LINUX__ // linux wants just SO_REUSEPORT + f = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&f, sizeof(f)); #endif #ifdef __LINUX__ - struct timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; - setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(const void *)&tv,sizeof(tv)); + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv)); #endif - if (bind(s,bindAddr,(bindAddr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) { - close(s); - //printf("%s\n",strerror(errno)); - return -1; - } + if (bind(s, bindAddr, (bindAddr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) { + close(s); + // printf("%s\n",strerror(errno)); + return -1; + } - return s; + return s; } static void shutdownSigHandler(int sig) { - s_run = false; + s_run = false; } -int main(int argc,char **argv) +int main(int argc, char** argv) { - std::vector threads; - std::vector sockets; - int v4Sock = -1,v6Sock = -1; + std::vector threads; + std::vector sockets; + int v4Sock = -1, v6Sock = -1; - signal(SIGTERM,shutdownSigHandler); - signal(SIGINT,shutdownSigHandler); - signal(SIGQUIT,shutdownSigHandler); - signal(SIGPIPE,SIG_IGN); - signal(SIGUSR1,SIG_IGN); - signal(SIGUSR2,SIG_IGN); - signal(SIGCHLD,SIG_IGN); + signal(SIGTERM, shutdownSigHandler); + signal(SIGINT, shutdownSigHandler); + signal(SIGQUIT, shutdownSigHandler); + signal(SIGPIPE, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + signal(SIGCHLD, SIG_IGN); - s_startTime = OSUtils::now(); - s_geoInit = false; + s_startTime = OSUtils::now(); + s_geoInit = false; - if (argc < 3) { - printf("Usage: zerotier-root " ZT_EOL_S); - return 1; - } + if (argc < 3) { + printf("Usage: zerotier-root " ZT_EOL_S); + return 1; + } - { - std::string myIdStr; - if (!OSUtils::readFile(argv[1],myIdStr)) { - printf("FATAL: cannot read identity.secret at %s" ZT_EOL_S,argv[1]); - return 1; - } - if (!s_self.fromString(myIdStr.c_str())) { - printf("FATAL: cannot read identity.secret at %s (invalid identity)" ZT_EOL_S,argv[1]); - return 1; - } - if (!s_self.hasPrivate()) { - printf("FATAL: cannot read identity.secret at %s (missing secret key)" ZT_EOL_S,argv[1]); - return 1; - } - } - { - std::string configStr; - if (!OSUtils::readFile(argv[2],configStr)) { - printf("FATAL: cannot read config file at %s" ZT_EOL_S,argv[2]); - return 1; - } - try { - s_config = json::parse(configStr); - } catch (std::exception &exc) { - printf("FATAL: config file at %s invalid: %s" ZT_EOL_S,argv[2],exc.what()); - return 1; - } catch ( ... ) { - printf("FATAL: config file at %s invalid: unknown exception" ZT_EOL_S,argv[2]); - return 1; - } - if (!s_config.is_object()) { - printf("FATAL: config file at %s invalid: does not contain a JSON object" ZT_EOL_S,argv[2]); - return 1; - } - } + { + std::string myIdStr; + if (! OSUtils::readFile(argv[1], myIdStr)) { + printf("FATAL: cannot read identity.secret at %s" ZT_EOL_S, argv[1]); + return 1; + } + if (! s_self.fromString(myIdStr.c_str())) { + printf("FATAL: cannot read identity.secret at %s (invalid identity)" ZT_EOL_S, argv[1]); + return 1; + } + if (! s_self.hasPrivate()) { + printf("FATAL: cannot read identity.secret at %s (missing secret key)" ZT_EOL_S, argv[1]); + return 1; + } + } + { + std::string configStr; + if (! OSUtils::readFile(argv[2], configStr)) { + printf("FATAL: cannot read config file at %s" ZT_EOL_S, argv[2]); + return 1; + } + try { + s_config = json::parse(configStr); + } + catch (std::exception& exc) { + printf("FATAL: config file at %s invalid: %s" ZT_EOL_S, argv[2], exc.what()); + return 1; + } + catch (...) { + printf("FATAL: config file at %s invalid: unknown exception" ZT_EOL_S, argv[2]); + return 1; + } + if (! s_config.is_object()) { + printf("FATAL: config file at %s invalid: does not contain a JSON object" ZT_EOL_S, argv[2]); + return 1; + } + } - try { - auto jport = s_config["port"]; - if (jport.is_array()) { - for(long i=0;i<(long)jport.size();++i) { - int port = jport[i]; - if ((port <= 0)||(port > 65535)) { - printf("FATAL: invalid port in config file %d" ZT_EOL_S,port); - return 1; - } - s_ports.push_back(port); - } - } else { - int port = jport; - if ((port <= 0)||(port > 65535)) { - printf("FATAL: invalid port in config file %d" ZT_EOL_S,port); - return 1; - } - s_ports.push_back(port); - } - } catch ( ... ) {} - if (s_ports.empty()) - s_ports.push_back(ZT_DEFAULT_PORT); - std::sort(s_ports.begin(),s_ports.end()); + try { + auto jport = s_config["port"]; + if (jport.is_array()) { + for (long i = 0; i < (long)jport.size(); ++i) { + int port = jport[i]; + if ((port <= 0) || (port > 65535)) { + printf("FATAL: invalid port in config file %d" ZT_EOL_S, port); + return 1; + } + s_ports.push_back(port); + } + } + else { + int port = jport; + if ((port <= 0) || (port > 65535)) { + printf("FATAL: invalid port in config file %d" ZT_EOL_S, port); + return 1; + } + s_ports.push_back(port); + } + } + catch (...) { + } + if (s_ports.empty()) + s_ports.push_back(ZT_DEFAULT_PORT); + std::sort(s_ports.begin(), s_ports.end()); - int httpPort = ZT_DEFAULT_PORT; - try { - httpPort = s_config["httpPort"]; - if ((httpPort <= 0)||(httpPort > 65535)) { - printf("FATAL: invalid HTTP port in config file %d" ZT_EOL_S,httpPort); - return 1; - } - } catch ( ... ) { - httpPort = ZT_DEFAULT_PORT; - } + int httpPort = ZT_DEFAULT_PORT; + try { + httpPort = s_config["httpPort"]; + if ((httpPort <= 0) || (httpPort > 65535)) { + printf("FATAL: invalid HTTP port in config file %d" ZT_EOL_S, httpPort); + return 1; + } + } + catch (...) { + httpPort = ZT_DEFAULT_PORT; + } - std::string planetFilePath; - try { - planetFilePath = s_config["planetFile"]; - } catch ( ... ) { - planetFilePath = ""; - } + std::string planetFilePath; + try { + planetFilePath = s_config["planetFile"]; + } + catch (...) { + planetFilePath = ""; + } - try { - s_statsRoot = s_config["statsRoot"]; - while ((s_statsRoot.length() > 0)&&(s_statsRoot[s_statsRoot.length()-1] == ZT_PATH_SEPARATOR)) - s_statsRoot = s_statsRoot.substr(0,s_statsRoot.length()-1); - if (s_statsRoot.length() > 0) - OSUtils::mkdir(s_statsRoot); - } catch ( ... ) { - s_statsRoot = ""; - } + try { + s_statsRoot = s_config["statsRoot"]; + while ((s_statsRoot.length() > 0) && (s_statsRoot[s_statsRoot.length() - 1] == ZT_PATH_SEPARATOR)) + s_statsRoot = s_statsRoot.substr(0, s_statsRoot.length() - 1); + if (s_statsRoot.length() > 0) + OSUtils::mkdir(s_statsRoot); + } + catch (...) { + s_statsRoot = ""; + } - s_relayMaxHops = ZT_RELAY_MAX_HOPS; - try { - s_relayMaxHops = s_config["relayMaxHops"]; - if (s_relayMaxHops > ZT_PROTO_MAX_HOPS) - s_relayMaxHops = ZT_PROTO_MAX_HOPS; - else if (s_relayMaxHops < 0) - s_relayMaxHops = 0; - } catch ( ... ) { - s_relayMaxHops = ZT_RELAY_MAX_HOPS; - } + s_relayMaxHops = ZT_RELAY_MAX_HOPS; + try { + s_relayMaxHops = s_config["relayMaxHops"]; + if (s_relayMaxHops > ZT_PROTO_MAX_HOPS) + s_relayMaxHops = ZT_PROTO_MAX_HOPS; + else if (s_relayMaxHops < 0) + s_relayMaxHops = 0; + } + catch (...) { + s_relayMaxHops = ZT_RELAY_MAX_HOPS; + } - try { - s_googleMapsAPIKey = s_config["googleMapsAPIKey"]; - std::string geoIpPath = s_config["geoIp"]; - if (geoIpPath.length() > 0) { - FILE *gf = fopen(geoIpPath.c_str(),"rb"); - if (gf) { - threads.emplace_back(std::thread([gf]() { - try { - char line[1024]; - line[1023] = 0; - while (fgets(line,sizeof(line)-1,gf)) { - InetAddress start,end; - float lat = 0.0F,lon = 0.0F; - int field = 0; - for(char *saveptr=nullptr,*f=Utils::stok(line,",\r\n",&saveptr);(f);f=Utils::stok(nullptr,",\r\n",&saveptr)) { - switch(field++) { - case 0: - start.fromString(f); - break; - case 1: - end.fromString(f); - break; - case 2: - lat = strtof(f,nullptr); - break; - case 3: - lon = strtof(f,nullptr); - break; - } - } - if ((start)&&(end)&&(start.ss_family == end.ss_family)&&(lat >= -90.0F)&&(lat <= 90.0F)&&(lon >= -180.0F)&&(lon <= 180.0F)) { - if (start.ss_family == AF_INET) { - s_geoIp4[std::pair< uint32_t,uint32_t >(ip4ToH32(start),ip4ToH32(end))] = std::pair< float,float >(lat,lon); - } else if (start.ss_family == AF_INET6) { - s_geoIp6[std::pair< std::array< uint64_t,2 >,std::array< uint64_t,2 > >(ip6ToH128(start),ip6ToH128(end))] = std::pair< float,float >(lat,lon); - } - } - } - s_geoInit = true; - } catch ( ... ) {} - fclose(gf); - })); - } - } - } catch ( ... ) {} + try { + s_googleMapsAPIKey = s_config["googleMapsAPIKey"]; + std::string geoIpPath = s_config["geoIp"]; + if (geoIpPath.length() > 0) { + FILE* gf = fopen(geoIpPath.c_str(), "rb"); + if (gf) { + threads.emplace_back(std::thread([gf]() { + try { + char line[1024]; + line[1023] = 0; + while (fgets(line, sizeof(line) - 1, gf)) { + InetAddress start, end; + float lat = 0.0F, lon = 0.0F; + int field = 0; + for (char *saveptr = nullptr, *f = Utils::stok(line, ",\r\n", &saveptr); (f); f = Utils::stok(nullptr, ",\r\n", &saveptr)) { + switch (field++) { + case 0: + start.fromString(f); + break; + case 1: + end.fromString(f); + break; + case 2: + lat = strtof(f, nullptr); + break; + case 3: + lon = strtof(f, nullptr); + break; + } + } + if ((start) && (end) && (start.ss_family == end.ss_family) && (lat >= -90.0F) && (lat <= 90.0F) && (lon >= -180.0F) && (lon <= 180.0F)) { + if (start.ss_family == AF_INET) { + s_geoIp4[std::pair(ip4ToH32(start), ip4ToH32(end))] = std::pair(lat, lon); + } + else if (start.ss_family == AF_INET6) { + s_geoIp6[std::pair, std::array >(ip6ToH128(start), ip6ToH128(end))] = std::pair(lat, lon); + } + } + } + s_geoInit = true; + } + catch (...) { + } + fclose(gf); + })); + } + } + } + catch (...) { + } - unsigned int ncores = std::thread::hardware_concurrency(); - if (ncores == 0) ncores = 1; + unsigned int ncores = std::thread::hardware_concurrency(); + if (ncores == 0) + ncores = 1; - s_run = true; + s_run = true; - threads.push_back(std::thread([]() { - std::vector< SharedPtr > toValidate; - while (s_run) { - { - std::lock_guard l(s_peersToValidate_l); - toValidate.swap(s_peersToValidate); - } - for(auto p=toValidate.begin();p!=toValidate.end();++p) { - if (likely(!(*p)->identityValidated)) { - if (likely((*p)->id.locallyValidate())) { - (*p)->identityValidated = true; - } else { - { - std::lock_guard p_l(s_peersByVirtAddr_l); - auto pp = s_peersByVirtAddr.find((*p)->id.address()); - if ((pp != s_peersByVirtAddr.end())&&(pp->second == *p)) { - s_peersByVirtAddr.erase(pp); - } - } - { - std::lock_guard p_l(s_peers_l); - for(auto pp=s_peers.begin();pp!=s_peers.end();++pp) { - if (*p == *pp) { - s_peers.erase(pp); - break; - } - } - } - } - } - } - toValidate.clear(); - usleep(1000); - } - })); + threads.push_back(std::thread([]() { + std::vector > toValidate; + while (s_run) { + { + std::lock_guard l(s_peersToValidate_l); + toValidate.swap(s_peersToValidate); + } + for (auto p = toValidate.begin(); p != toValidate.end(); ++p) { + if (likely(! (*p)->identityValidated)) { + if (likely((*p)->id.locallyValidate())) { + (*p)->identityValidated = true; + } + else { + { + std::lock_guard p_l(s_peersByVirtAddr_l); + auto pp = s_peersByVirtAddr.find((*p)->id.address()); + if ((pp != s_peersByVirtAddr.end()) && (pp->second == *p)) { + s_peersByVirtAddr.erase(pp); + } + } + { + std::lock_guard p_l(s_peers_l); + for (auto pp = s_peers.begin(); pp != s_peers.end(); ++pp) { + if (*p == *pp) { + s_peers.erase(pp); + break; + } + } + } + } + } + } + toValidate.clear(); + usleep(1000); + } + })); - for(auto port=s_ports.begin();port!=s_ports.end();++port) { - for(unsigned int tn=0;tnunsafeData(),pkt->capacity(),RECVFROM_FLAGS,(struct sockaddr *)&in6,&sl); - if (pl > 0) { - if ((pl >= ZT_PROTO_MIN_FRAGMENT_LENGTH)&&(pl <= ZT_PROTO_MAX_PACKET_LENGTH)) { - try { - pkt->setSize((unsigned int)pl); - handlePacket(s6,reinterpret_cast(&in6),*pkt); - } catch (std::exception &exc) { - char ipstr[128]; - printf("WARNING: unexpected exception handling packet from %s: %s" ZT_EOL_S,reinterpret_cast(&in6)->toString(ipstr),exc.what()); - } catch (int exc) { - char ipstr[128]; - printf("WARNING: unexpected exception handling packet from %s: ZT exception code %d" ZT_EOL_S,reinterpret_cast(&in6)->toString(ipstr),exc); - } catch ( ... ) { - char ipstr[128]; - printf("WARNING: unexpected exception handling packet from %s: unknown exception" ZT_EOL_S,reinterpret_cast(&in6)->toString(ipstr)); - } - } - } else if (!s_run) { - break; - } - } - delete pkt; - })); + threads.push_back(std::thread([s6, s4]() { + struct sockaddr_in6 in6; + Packet* pkt = new Packet(); + for (;;) { + memset(&in6, 0, sizeof(in6)); + socklen_t sl = sizeof(in6); + const int pl = (int)recvfrom(s6, pkt->unsafeData(), pkt->capacity(), RECVFROM_FLAGS, (struct sockaddr*)&in6, &sl); + if (pl > 0) { + if ((pl >= ZT_PROTO_MIN_FRAGMENT_LENGTH) && (pl <= ZT_PROTO_MAX_PACKET_LENGTH)) { + try { + pkt->setSize((unsigned int)pl); + handlePacket(s6, reinterpret_cast(&in6), *pkt); + } + catch (std::exception& exc) { + char ipstr[128]; + printf("WARNING: unexpected exception handling packet from %s: %s" ZT_EOL_S, reinterpret_cast(&in6)->toString(ipstr), exc.what()); + } + catch (int exc) { + char ipstr[128]; + printf("WARNING: unexpected exception handling packet from %s: ZT exception code %d" ZT_EOL_S, reinterpret_cast(&in6)->toString(ipstr), exc); + } + catch (...) { + char ipstr[128]; + printf("WARNING: unexpected exception handling packet from %s: unknown exception" ZT_EOL_S, reinterpret_cast(&in6)->toString(ipstr)); + } + } + } + else if (! s_run) { + break; + } + } + delete pkt; + })); - threads.push_back(std::thread([s6,s4]() { - struct sockaddr_in in4; - Packet *pkt = new Packet(); - for(;;) { - memset(&in4,0,sizeof(in4)); - socklen_t sl = sizeof(in4); - const int pl = (int)recvfrom(s4,pkt->unsafeData(),pkt->capacity(),RECVFROM_FLAGS,(struct sockaddr *)&in4,&sl); - if (pl > 0) { - if ((pl >= ZT_PROTO_MIN_FRAGMENT_LENGTH)&&(pl <= ZT_PROTO_MAX_PACKET_LENGTH)) { - try { - pkt->setSize((unsigned int)pl); - handlePacket(s4,reinterpret_cast(&in4),*pkt); - } catch (std::exception &exc) { - char ipstr[128]; - printf("WARNING: unexpected exception handling packet from %s: %s" ZT_EOL_S,reinterpret_cast(&in4)->toString(ipstr),exc.what()); - } catch (int exc) { - char ipstr[128]; - printf("WARNING: unexpected exception handling packet from %s: ZT exception code %d" ZT_EOL_S,reinterpret_cast(&in4)->toString(ipstr),exc); - } catch ( ... ) { - char ipstr[128]; - printf("WARNING: unexpected exception handling packet from %s: unknown exception" ZT_EOL_S,reinterpret_cast(&in4)->toString(ipstr)); - } - } - } else if (!s_run) { - break; - } - } - delete pkt; - })); - } - } + threads.push_back(std::thread([s6, s4]() { + struct sockaddr_in in4; + Packet* pkt = new Packet(); + for (;;) { + memset(&in4, 0, sizeof(in4)); + socklen_t sl = sizeof(in4); + const int pl = (int)recvfrom(s4, pkt->unsafeData(), pkt->capacity(), RECVFROM_FLAGS, (struct sockaddr*)&in4, &sl); + if (pl > 0) { + if ((pl >= ZT_PROTO_MIN_FRAGMENT_LENGTH) && (pl <= ZT_PROTO_MAX_PACKET_LENGTH)) { + try { + pkt->setSize((unsigned int)pl); + handlePacket(s4, reinterpret_cast(&in4), *pkt); + } + catch (std::exception& exc) { + char ipstr[128]; + printf("WARNING: unexpected exception handling packet from %s: %s" ZT_EOL_S, reinterpret_cast(&in4)->toString(ipstr), exc.what()); + } + catch (int exc) { + char ipstr[128]; + printf("WARNING: unexpected exception handling packet from %s: ZT exception code %d" ZT_EOL_S, reinterpret_cast(&in4)->toString(ipstr), exc); + } + catch (...) { + char ipstr[128]; + printf("WARNING: unexpected exception handling packet from %s: unknown exception" ZT_EOL_S, reinterpret_cast(&in4)->toString(ipstr)); + } + } + } + else if (! s_run) { + break; + } + } + delete pkt; + })); + } + } - // A minimal read-only local API for monitoring and status queries - httplib::Server apiServ; - threads.push_back(std::thread([&apiServ,httpPort]() { - // Human readable status page - apiServ.Get("/",[](const httplib::Request &req,httplib::Response &res) { - std::ostringstream o; - o << "ZeroTier Root Server " << ZEROTIER_ONE_VERSION_MAJOR << '.' << ZEROTIER_ONE_VERSION_MINOR << '.' << ZEROTIER_ONE_VERSION_REVISION << ZT_EOL_S; - o << "(c)2019 ZeroTier, Inc." ZT_EOL_S "Licensed under the ZeroTier BSL 1.1" ZT_EOL_S ZT_EOL_S; - s_peersByVirtAddr_l.lock(); - o << "Peers Online: " << s_peersByVirtAddr.size() << ZT_EOL_S; - s_peersByVirtAddr_l.unlock(); - res.set_content(o.str(),"text/plain"); - }); + // A minimal read-only local API for monitoring and status queries + httplib::Server apiServ; + threads.push_back(std::thread([&apiServ, httpPort]() { + // Human readable status page + apiServ.Get("/", [](const httplib::Request& req, httplib::Response& res) { + std::ostringstream o; + o << "ZeroTier Root Server " << ZEROTIER_ONE_VERSION_MAJOR << '.' << ZEROTIER_ONE_VERSION_MINOR << '.' << ZEROTIER_ONE_VERSION_REVISION << ZT_EOL_S; + o << "(c)2019 ZeroTier, Inc." ZT_EOL_S "Licensed under the ZeroTier BSL 1.1" ZT_EOL_S ZT_EOL_S; + s_peersByVirtAddr_l.lock(); + o << "Peers Online: " << s_peersByVirtAddr.size() << ZT_EOL_S; + s_peersByVirtAddr_l.unlock(); + res.set_content(o.str(), "text/plain"); + }); - apiServ.Get("/metrics",[](const httplib::Request &req, httplib::Response &res) { - std::ostringstream o; - int64_t now = OSUtils::now(); + apiServ.Get("/metrics", [](const httplib::Request& req, httplib::Response& res) { + std::ostringstream o; + int64_t now = OSUtils::now(); - char buf[11]; - const char *root_id = s_self.address().toString(buf); - o << "# HELP root_peers_online Number of active peers online" << ZT_EOL_S; - o << "# TYPE root_peers_online gauge" << ZT_EOL_S; - s_peersByVirtAddr_l.lock(); - o << "root_peers_online{root_id=\"" << root_id << "\"} " << s_peersByVirtAddr.size() << ZT_EOL_S; - s_peersByVirtAddr_l.unlock(); - o << "# HELP root_input_rate Input rate MiB/s" << ZT_EOL_S; - o << "# TYPE root_input_rate gauge" << ZT_EOL_S; - o << "root_input_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_inputRate.perSecond(now)/1048576.0) << ZT_EOL_S; - o << "# HELP root_output_rate Output rate MiB/s" << ZT_EOL_S; - o << "# TYPE root_output_rate gauge" << ZT_EOL_S; - o << "root_output_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_outputRate.perSecond(now)/1048576.0) << ZT_EOL_S; - o << "# HELP root_forwarded_rate Forwarded packet rate MiB/s" << ZT_EOL_S; - o << "# TYPE root_forwarded_rate gauge" << ZT_EOL_S; - o << "root_forwarded_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_forwardRate.perSecond(now)/1048576.0) << ZT_EOL_S; - o << "# HELP root_discarded_rate Discarded forwards MiB/s" << ZT_EOL_S; - o << "# TYPE root_discarded_rate gauge" << ZT_EOL_S; - o << "root_discarded_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_discardedForwardRate.perSecond(now)/1048576.0) << ZT_EOL_S; + char buf[11]; + const char* root_id = s_self.address().toString(buf); + o << "# HELP root_peers_online Number of active peers online" << ZT_EOL_S; + o << "# TYPE root_peers_online gauge" << ZT_EOL_S; + s_peersByVirtAddr_l.lock(); + o << "root_peers_online{root_id=\"" << root_id << "\"} " << s_peersByVirtAddr.size() << ZT_EOL_S; + s_peersByVirtAddr_l.unlock(); + o << "# HELP root_input_rate Input rate MiB/s" << ZT_EOL_S; + o << "# TYPE root_input_rate gauge" << ZT_EOL_S; + o << "root_input_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_inputRate.perSecond(now) / 1048576.0) << ZT_EOL_S; + o << "# HELP root_output_rate Output rate MiB/s" << ZT_EOL_S; + o << "# TYPE root_output_rate gauge" << ZT_EOL_S; + o << "root_output_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_outputRate.perSecond(now) / 1048576.0) << ZT_EOL_S; + o << "# HELP root_forwarded_rate Forwarded packet rate MiB/s" << ZT_EOL_S; + o << "# TYPE root_forwarded_rate gauge" << ZT_EOL_S; + o << "root_forwarded_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_forwardRate.perSecond(now) / 1048576.0) << ZT_EOL_S; + o << "# HELP root_discarded_rate Discarded forwards MiB/s" << ZT_EOL_S; + o << "# TYPE root_discarded_rate gauge" << ZT_EOL_S; + o << "root_discarded_rate{root_id=\"" << root_id << "\"} " << std::setprecision(5) << (s_discardedForwardRate.perSecond(now) / 1048576.0) << ZT_EOL_S; - res.set_content(o.str(), "text/plain"); - }); + res.set_content(o.str(), "text/plain"); + }); - // Peer list for compatibility with software that monitors regular nodes - apiServ.Get("/peer",[](const httplib::Request &req,httplib::Response &res) { - char tmp[256]; - std::ostringstream o; - o << '['; - try { - bool first = true; - std::lock_guard l(s_peers_l); - for(auto p=s_peers.begin();p!=s_peers.end();++p) { - if (first) - first = false; - else o << ','; - o << - "{\"address\":\"" << (*p)->id.address().toString(tmp) << "\"" - ",\"latency\":-1" - ",\"paths\":["; - if ((*p)->v4s >= 0) { - o << - "{\"active\":true" - ",\"address\":\"" << (*p)->ip4.toIpString(tmp) << "\\/" << (*p)->ip4.port() << "\"" - ",\"expired\":false" - ",\"lastReceive\":" << (*p)->lastReceive << - ",\"lastSend\":" << (*p)->lastSend << - ",\"preferred\":true" - ",\"trustedPathId\":0}"; - } - if ((*p)->v6s >= 0) { - if ((*p)->v4s >= 0) - o << ','; - o << - "{\"active\":true" - ",\"address\":\"" << (*p)->ip6.toIpString(tmp) << "\\/" << (*p)->ip6.port() << "\"" - ",\"expired\":false" - ",\"lastReceive\":" << (*p)->lastReceive << - ",\"lastSend\":" << (*p)->lastSend << - ",\"preferred\":" << (((*p)->ip4) ? "false" : "true") << - ",\"trustedPathId\":0}"; - } - o << "]" - ",\"role\":\"LEAF\"" - ",\"version\":\"" << (*p)->vMajor << '.' << (*p)->vMinor << '.' << (*p)->vRev << "\"" - ",\"versionMajor\":" << (*p)->vMajor << - ",\"versionMinor\":" << (*p)->vMinor << - ",\"versionRev\":" << (*p)->vRev << "}"; - } - } catch ( ... ) {} - o << ']'; - res.set_content(o.str(),"application/json"); - }); + // Peer list for compatibility with software that monitors regular nodes + apiServ.Get("/peer", [](const httplib::Request& req, httplib::Response& res) { + char tmp[256]; + std::ostringstream o; + o << '['; + try { + bool first = true; + std::lock_guard l(s_peers_l); + for (auto p = s_peers.begin(); p != s_peers.end(); ++p) { + if (first) + first = false; + else + o << ','; + o << "{\"address\":\"" << (*p)->id.address().toString(tmp) + << "\"" + ",\"latency\":-1" + ",\"paths\":["; + if ((*p)->v4s >= 0) { + o << "{\"active\":true" + ",\"address\":\"" + << (*p)->ip4.toIpString(tmp) << "\\/" << (*p)->ip4.port() + << "\"" + ",\"expired\":false" + ",\"lastReceive\":" + << (*p)->lastReceive << ",\"lastSend\":" << (*p)->lastSend + << ",\"preferred\":true" + ",\"trustedPathId\":0}"; + } + if ((*p)->v6s >= 0) { + if ((*p)->v4s >= 0) + o << ','; + o << "{\"active\":true" + ",\"address\":\"" + << (*p)->ip6.toIpString(tmp) << "\\/" << (*p)->ip6.port() + << "\"" + ",\"expired\":false" + ",\"lastReceive\":" + << (*p)->lastReceive << ",\"lastSend\":" << (*p)->lastSend << ",\"preferred\":" << (((*p)->ip4) ? "false" : "true") << ",\"trustedPathId\":0}"; + } + o << "]" + ",\"role\":\"LEAF\"" + ",\"version\":\"" + << (*p)->vMajor << '.' << (*p)->vMinor << '.' << (*p)->vRev + << "\"" + ",\"versionMajor\":" + << (*p)->vMajor << ",\"versionMinor\":" << (*p)->vMinor << ",\"versionRev\":" << (*p)->vRev << "}"; + } + } + catch (...) { + } + o << ']'; + res.set_content(o.str(), "application/json"); + }); - // GeoIP map if enabled - apiServ.Get("/map",[](const httplib::Request &req,httplib::Response &res) { - char tmp[4096]; - if (!s_geoInit) { - res.set_content("Not enabled or GeoIP CSV file not finished reading.","text/plain"); - return; - } - std::ostringstream o; - o << ZT_GEOIP_HTML_HEAD; - try { - bool firstCoord = true; - std::pair< uint32_t,uint32_t > k4(0,0xffffffff); - std::pair< std::array< uint64_t,2 >,std::array< uint64_t,2 > > k6; - k6.second[0] = 0xffffffffffffffffULL; k6.second[1] = 0xffffffffffffffffULL; + // GeoIP map if enabled + apiServ.Get("/map", [](const httplib::Request& req, httplib::Response& res) { + char tmp[4096]; + if (! s_geoInit) { + res.set_content("Not enabled or GeoIP CSV file not finished reading.", "text/plain"); + return; + } + std::ostringstream o; + o << ZT_GEOIP_HTML_HEAD; + try { + bool firstCoord = true; + std::pair k4(0, 0xffffffff); + std::pair, std::array > k6; + k6.second[0] = 0xffffffffffffffffULL; + k6.second[1] = 0xffffffffffffffffULL; - std::unordered_map< InetAddress,std::set
,InetAddressHasher > ips; - { - std::lock_guard l(s_peers_l); - for(auto p=s_peers.begin();p!=s_peers.end();++p) { - if ((*p)->v4s >= 0) - ips[(*p)->ip4].insert((*p)->id.address()); - if ((*p)->v6s >= 0) - ips[(*p)->ip6].insert((*p)->id.address()); - } - } + std::unordered_map, InetAddressHasher> ips; + { + std::lock_guard l(s_peers_l); + for (auto p = s_peers.begin(); p != s_peers.end(); ++p) { + if ((*p)->v4s >= 0) + ips[(*p)->ip4].insert((*p)->id.address()); + if ((*p)->v6s >= 0) + ips[(*p)->ip6].insert((*p)->id.address()); + } + } - for(auto p=ips.begin();p!=ips.end();++p) { - if (p->first.isV4()) { - k4.first = ip4ToH32(p->first); - auto geo = std::map< std::pair< uint32_t,uint32_t >,std::pair< float,float > >::reverse_iterator(s_geoIp4.upper_bound(k4)); - uint32_t bestRangeSize = 0xffffffff; - std::pair< float,float > bestRangeLatLon; - while (geo != s_geoIp4.rend()) { - if ((geo->first.first <= k4.first)&&(geo->first.second >= k4.first)) { - uint32_t range = geo->first.second - geo->first.first; - if (range <= bestRangeSize) { - bestRangeSize = range; - bestRangeLatLon = geo->second; - } - } else if ((geo->first.first < k4.first)&&(geo->first.second < k4.first)) { - break; - } - ++geo; - } - if (bestRangeSize != 0xffffffff) { - if (!firstCoord) - o << ','; - firstCoord = false; - o << "{lat:" << bestRangeLatLon.first << ",lng:" << bestRangeLatLon.second << ",_l:\""; - bool firstAddr = true; - for(auto a=p->second.begin();a!=p->second.end();++a) { - if (!firstAddr) - o << ','; - o << a->toString(tmp); - firstAddr = false; - } - o << "\"}"; - } - } else if (p->first.isV6()) { - k6.first = ip6ToH128(p->first); - auto geo = std::map< std::pair< std::array< uint64_t,2 >,std::array< uint64_t,2 > >,std::pair< float,float > >::reverse_iterator(s_geoIp6.upper_bound(k6)); - while (geo != s_geoIp6.rend()) { - if ((geo->first.first <= k6.first)&&(geo->first.second >= k6.first)) { - if (!firstCoord) - o << ','; - firstCoord = false; - o << "{lat:" << geo->second.first << ",lng:" << geo->second.second << ",_l:\""; - bool firstAddr = true; - for(auto a=p->second.begin();a!=p->second.end();++a) { - if (!firstAddr) - o << ','; - o << a->toString(tmp); - firstAddr = false; - } - o << "\"}"; - break; - } else if ((geo->first.first < k6.first)&&(geo->first.second < k6.first)) { - break; - } - ++geo; - } - } - } - } catch ( ... ) { - res.set_content("Internal error: unexpected exception resolving GeoIP locations","text/plain"); - return; - } - OSUtils::ztsnprintf(tmp,sizeof(tmp),ZT_GEOIP_HTML_TAIL,s_googleMapsAPIKey.c_str()); - o << tmp; - res.set_content(o.str(),"text/html"); - }); + for (auto p = ips.begin(); p != ips.end(); ++p) { + if (p->first.isV4()) { + k4.first = ip4ToH32(p->first); + auto geo = std::map, std::pair >::reverse_iterator(s_geoIp4.upper_bound(k4)); + uint32_t bestRangeSize = 0xffffffff; + std::pair bestRangeLatLon; + while (geo != s_geoIp4.rend()) { + if ((geo->first.first <= k4.first) && (geo->first.second >= k4.first)) { + uint32_t range = geo->first.second - geo->first.first; + if (range <= bestRangeSize) { + bestRangeSize = range; + bestRangeLatLon = geo->second; + } + } + else if ((geo->first.first < k4.first) && (geo->first.second < k4.first)) { + break; + } + ++geo; + } + if (bestRangeSize != 0xffffffff) { + if (! firstCoord) + o << ','; + firstCoord = false; + o << "{lat:" << bestRangeLatLon.first << ",lng:" << bestRangeLatLon.second << ",_l:\""; + bool firstAddr = true; + for (auto a = p->second.begin(); a != p->second.end(); ++a) { + if (! firstAddr) + o << ','; + o << a->toString(tmp); + firstAddr = false; + } + o << "\"}"; + } + } + else if (p->first.isV6()) { + k6.first = ip6ToH128(p->first); + auto geo = std::map, std::array >, std::pair >::reverse_iterator(s_geoIp6.upper_bound(k6)); + while (geo != s_geoIp6.rend()) { + if ((geo->first.first <= k6.first) && (geo->first.second >= k6.first)) { + if (! firstCoord) + o << ','; + firstCoord = false; + o << "{lat:" << geo->second.first << ",lng:" << geo->second.second << ",_l:\""; + bool firstAddr = true; + for (auto a = p->second.begin(); a != p->second.end(); ++a) { + if (! firstAddr) + o << ','; + o << a->toString(tmp); + firstAddr = false; + } + o << "\"}"; + break; + } + else if ((geo->first.first < k6.first) && (geo->first.second < k6.first)) { + break; + } + ++geo; + } + } + } + } + catch (...) { + res.set_content("Internal error: unexpected exception resolving GeoIP locations", "text/plain"); + return; + } + OSUtils::ztsnprintf(tmp, sizeof(tmp), ZT_GEOIP_HTML_TAIL, s_googleMapsAPIKey.c_str()); + o << tmp; + res.set_content(o.str(), "text/html"); + }); - apiServ.listen("127.0.0.1",httpPort,0); - })); + apiServ.listen("127.0.0.1", httpPort, 0); + })); - // In the main thread periodically clean stuff up - int64_t lastCleaned = 0; - int64_t lastWroteStats = 0; - while (s_run) { - sleep(1); + // In the main thread periodically clean stuff up + int64_t lastCleaned = 0; + int64_t lastWroteStats = 0; + while (s_run) { + sleep(1); - const int64_t now = OSUtils::now(); + const int64_t now = OSUtils::now(); - if ((now - lastCleaned) > 300000) { - lastCleaned = now; + if ((now - lastCleaned) > 300000) { + lastCleaned = now; - // Old multicast subscription cleanup - { - std::lock_guard l(s_multicastSubscriptions_l); - for(auto a=s_multicastSubscriptions.begin();a!=s_multicastSubscriptions.end();) { - for(auto b=a->second.begin();b!=a->second.end();) { - for(auto c=b->second.begin();c!=b->second.end();) { - if ((now - c->second) > ZT_MULTICAST_LIKE_EXPIRE) - b->second.erase(c++); - else ++c; - } - if (b->second.empty()) - a->second.erase(b++); - else ++b; - } - if (a->second.empty()) - s_multicastSubscriptions.erase(a++); - else ++a; - } - } + // Old multicast subscription cleanup + { + std::lock_guard l(s_multicastSubscriptions_l); + for (auto a = s_multicastSubscriptions.begin(); a != s_multicastSubscriptions.end();) { + for (auto b = a->second.begin(); b != a->second.end();) { + for (auto c = b->second.begin(); c != b->second.end();) { + if ((now - c->second) > ZT_MULTICAST_LIKE_EXPIRE) + b->second.erase(c++); + else + ++c; + } + if (b->second.empty()) + a->second.erase(b++); + else + ++b; + } + if (a->second.empty()) + s_multicastSubscriptions.erase(a++); + else + ++a; + } + } - try { - std::vector< SharedPtr > toRemove; - toRemove.reserve(1024); - { - std::lock_guard pbi_l(s_peers_l); - std::vector< SharedPtr > newPeers; - newPeers.reserve(s_peers.size()); - for(auto p=s_peers.begin();p!=s_peers.end();++p) { - if ((now - (*p)->lastReceive) > ZT_PEER_ACTIVITY_TIMEOUT) { - toRemove.emplace_back(); - p->swap(toRemove.back()); - } else { - newPeers.emplace_back(); - p->swap(newPeers.back()); - } - } - newPeers.swap(s_peers); - } - for(auto p=toRemove.begin();p!=toRemove.end();++p) { - { - std::lock_guard pbv_l(s_peersByVirtAddr_l); - auto pbv = s_peersByVirtAddr.find((*p)->id.address()); - if ((pbv != s_peersByVirtAddr.end())&&(pbv->second == *p)) { - s_peersByVirtAddr.erase(pbv); - } - } - } - } catch ( ... ) {} + try { + std::vector > toRemove; + toRemove.reserve(1024); + { + std::lock_guard pbi_l(s_peers_l); + std::vector > newPeers; + newPeers.reserve(s_peers.size()); + for (auto p = s_peers.begin(); p != s_peers.end(); ++p) { + if ((now - (*p)->lastReceive) > ZT_PEER_ACTIVITY_TIMEOUT) { + toRemove.emplace_back(); + p->swap(toRemove.back()); + } + else { + newPeers.emplace_back(); + p->swap(newPeers.back()); + } + } + newPeers.swap(s_peers); + } + for (auto p = toRemove.begin(); p != toRemove.end(); ++p) { + { + std::lock_guard pbv_l(s_peersByVirtAddr_l); + auto pbv = s_peersByVirtAddr.find((*p)->id.address()); + if ((pbv != s_peersByVirtAddr.end()) && (pbv->second == *p)) { + s_peersByVirtAddr.erase(pbv); + } + } + } + } + catch (...) { + } - // Remove old rendezvous entries - { - std::lock_guard l(s_rendezvousTracking_l); - for(auto lr=s_rendezvousTracking.begin();lr!=s_rendezvousTracking.end();) { - if ((now - lr->second.ts) > ZT_PEER_ACTIVITY_TIMEOUT) - s_rendezvousTracking.erase(lr++); - else ++lr; - } - } - } + // Remove old rendezvous entries + { + std::lock_guard l(s_rendezvousTracking_l); + for (auto lr = s_rendezvousTracking.begin(); lr != s_rendezvousTracking.end();) { + if ((now - lr->second.ts) > ZT_PEER_ACTIVITY_TIMEOUT) + s_rendezvousTracking.erase(lr++); + else + ++lr; + } + } + } - // Write stats if configured to do so, and periodically refresh planet file (if any) - if (((now - lastWroteStats) > 15000)&&(s_statsRoot.length() > 0)) { - lastWroteStats = now; + // Write stats if configured to do so, and periodically refresh planet file (if any) + if (((now - lastWroteStats) > 15000) && (s_statsRoot.length() > 0)) { + lastWroteStats = now; - try { - if (planetFilePath.length() > 0) { - std::string planetData; - if ((OSUtils::readFile(planetFilePath.c_str(),planetData))&&(planetData.length() > 0)) { - std::lock_guard pl(s_planet_l); - s_planet = planetData; - } - } - } catch ( ... ) { - std::lock_guard pl(s_planet_l); - s_planet.clear(); - } + try { + if (planetFilePath.length() > 0) { + std::string planetData; + if ((OSUtils::readFile(planetFilePath.c_str(), planetData)) && (planetData.length() > 0)) { + std::lock_guard pl(s_planet_l); + s_planet = planetData; + } + } + } + catch (...) { + std::lock_guard pl(s_planet_l); + s_planet.clear(); + } - std::string peersFilePath(s_statsRoot); - peersFilePath.append("/.peers.tmp"); - FILE *pf = fopen(peersFilePath.c_str(),"wb"); - if (pf) { - std::vector< SharedPtr > sp; - { - std::lock_guard pbi_l(s_peers_l); - sp.reserve(s_peers.size()); - for(auto p=s_peers.begin();p!=s_peers.end();++p) { - sp.emplace_back(*p); - } - } - std::sort(sp.begin(),sp.end(),[](const SharedPtr &a,const SharedPtr &b) { return (a->id < b->id); }); + std::string peersFilePath(s_statsRoot); + peersFilePath.append("/.peers.tmp"); + FILE* pf = fopen(peersFilePath.c_str(), "wb"); + if (pf) { + std::vector > sp; + { + std::lock_guard pbi_l(s_peers_l); + sp.reserve(s_peers.size()); + for (auto p = s_peers.begin(); p != s_peers.end(); ++p) { + sp.emplace_back(*p); + } + } + std::sort(sp.begin(), sp.end(), [](const SharedPtr& a, const SharedPtr& b) { return (a->id < b->id); }); - fprintf(pf,"Address %21s %45s %10s %6s %10s" ZT_EOL_S,"IPv4","IPv6","Age(sec)","Vers","Fwd(KiB/s)"); - { - char ip4[128],ip6[128],ver[128]; - for(auto p=sp.begin();p!=sp.end();++p) { - if ((*p)->v4s >= 0) { - (*p)->ip4.toString(ip4); - } else { - ip4[0] = '-'; - ip4[1] = 0; - } - if ((*p)->v6s >= 0) { - (*p)->ip6.toString(ip6); - } else { - ip6[0] = '-'; - ip6[1] = 0; - } - OSUtils::ztsnprintf(ver,sizeof(ver),"%d.%d.%d",(*p)->vMajor,(*p)->vMinor,(*p)->vRev); - fprintf(pf,"%.10llx %21s %45s %10.4f %6s" ZT_EOL_S, - (unsigned long long)(*p)->id.address().toInt(), - ip4, - ip6, - fabs((double)(now - (*p)->lastReceive) / 1000.0), - ver); - } - } + fprintf(pf, "Address %21s %45s %10s %6s %10s" ZT_EOL_S, "IPv4", "IPv6", "Age(sec)", "Vers", "Fwd(KiB/s)"); + { + char ip4[128], ip6[128], ver[128]; + for (auto p = sp.begin(); p != sp.end(); ++p) { + if ((*p)->v4s >= 0) { + (*p)->ip4.toString(ip4); + } + else { + ip4[0] = '-'; + ip4[1] = 0; + } + if ((*p)->v6s >= 0) { + (*p)->ip6.toString(ip6); + } + else { + ip6[0] = '-'; + ip6[1] = 0; + } + OSUtils::ztsnprintf(ver, sizeof(ver), "%d.%d.%d", (*p)->vMajor, (*p)->vMinor, (*p)->vRev); + fprintf(pf, "%.10llx %21s %45s %10.4f %6s" ZT_EOL_S, (unsigned long long)(*p)->id.address().toInt(), ip4, ip6, fabs((double)(now - (*p)->lastReceive) / 1000.0), ver); + } + } - fclose(pf); - std::string peersFilePath2(s_statsRoot); - peersFilePath2.append("/peers"); - OSUtils::rm(peersFilePath2); - OSUtils::rename(peersFilePath.c_str(),peersFilePath2.c_str()); - } + fclose(pf); + std::string peersFilePath2(s_statsRoot); + peersFilePath2.append("/peers"); + OSUtils::rm(peersFilePath2); + OSUtils::rename(peersFilePath.c_str(), peersFilePath2.c_str()); + } - std::string statsFilePath(s_statsRoot); - statsFilePath.append("/.stats.tmp"); - FILE *sf = fopen(statsFilePath.c_str(),"wb"); - if (sf) { - fprintf(sf,"Uptime (seconds) : %ld" ZT_EOL_S,(long)((now - s_startTime) / 1000)); - s_peersByVirtAddr_l.lock(); - fprintf(sf,"Peers : %llu" ZT_EOL_S,(unsigned long long)s_peersByVirtAddr.size()); - s_peersByVirtAddr_l.unlock(); - s_rendezvousTracking_l.lock(); - uint64_t unsuccessfulp2p = 0; - for(auto lr=s_rendezvousTracking.begin();lr!=s_rendezvousTracking.end();++lr) { - if (lr->second.count > 6) // 6 == two attempts per edge, one for each direction - ++unsuccessfulp2p; - } - fprintf(sf,"Recent P2P Graph Edges : %llu" ZT_EOL_S,(unsigned long long)s_rendezvousTracking.size()); - if (s_rendezvousTracking.empty()) { - fprintf(sf,"Recent P2P Success Rate : 100.0000%%" ZT_EOL_S); - } else { - fprintf(sf,"Recent P2P Success Rate : %.4f%%" ZT_EOL_S,(1.0 - ((double)unsuccessfulp2p / (double)s_rendezvousTracking.size())) * 100.0); - } - s_rendezvousTracking_l.unlock(); - fprintf(sf,"Input (MiB/s) : %.4f" ZT_EOL_S,s_inputRate.perSecond(now) / 1048576.0); - fprintf(sf,"Output (MiB/s) : %.4f" ZT_EOL_S,s_outputRate.perSecond(now) / 1048576.0); - fprintf(sf,"Forwarded (MiB/s) : %.4f" ZT_EOL_S,s_forwardRate.perSecond(now) / 1048576.0); - fprintf(sf,"Discarded Forward (MiB/s) : %.4f" ZT_EOL_S,s_discardedForwardRate.perSecond(now) / 1048576.0); + std::string statsFilePath(s_statsRoot); + statsFilePath.append("/.stats.tmp"); + FILE* sf = fopen(statsFilePath.c_str(), "wb"); + if (sf) { + fprintf(sf, "Uptime (seconds) : %ld" ZT_EOL_S, (long)((now - s_startTime) / 1000)); + s_peersByVirtAddr_l.lock(); + fprintf(sf, "Peers : %llu" ZT_EOL_S, (unsigned long long)s_peersByVirtAddr.size()); + s_peersByVirtAddr_l.unlock(); + s_rendezvousTracking_l.lock(); + uint64_t unsuccessfulp2p = 0; + for (auto lr = s_rendezvousTracking.begin(); lr != s_rendezvousTracking.end(); ++lr) { + if (lr->second.count > 6) // 6 == two attempts per edge, one for each direction + ++unsuccessfulp2p; + } + fprintf(sf, "Recent P2P Graph Edges : %llu" ZT_EOL_S, (unsigned long long)s_rendezvousTracking.size()); + if (s_rendezvousTracking.empty()) { + fprintf(sf, "Recent P2P Success Rate : 100.0000%%" ZT_EOL_S); + } + else { + fprintf(sf, "Recent P2P Success Rate : %.4f%%" ZT_EOL_S, (1.0 - ((double)unsuccessfulp2p / (double)s_rendezvousTracking.size())) * 100.0); + } + s_rendezvousTracking_l.unlock(); + fprintf(sf, "Input (MiB/s) : %.4f" ZT_EOL_S, s_inputRate.perSecond(now) / 1048576.0); + fprintf(sf, "Output (MiB/s) : %.4f" ZT_EOL_S, s_outputRate.perSecond(now) / 1048576.0); + fprintf(sf, "Forwarded (MiB/s) : %.4f" ZT_EOL_S, s_forwardRate.perSecond(now) / 1048576.0); + fprintf(sf, "Discarded Forward (MiB/s) : %.4f" ZT_EOL_S, s_discardedForwardRate.perSecond(now) / 1048576.0); - fclose(sf); - std::string statsFilePath2(s_statsRoot); - statsFilePath2.append("/stats"); - OSUtils::rm(statsFilePath2); - OSUtils::rename(statsFilePath.c_str(),statsFilePath2.c_str()); - } - } - } + fclose(sf); + std::string statsFilePath2(s_statsRoot); + statsFilePath2.append("/stats"); + OSUtils::rm(statsFilePath2); + OSUtils::rename(statsFilePath.c_str(), statsFilePath2.c_str()); + } + } + } - // If we received a kill signal, close everything and wait - // for threads to die before exiting. - s_run = false; // sanity check - apiServ.stop(); - for(auto s=sockets.begin();s!=sockets.end();++s) { - shutdown(*s,SHUT_RDWR); - close(*s); - } - for(auto t=threads.begin();t!=threads.end();++t) - t->join(); + // If we received a kill signal, close everything and wait + // for threads to die before exiting. + s_run = false; // sanity check + apiServ.stop(); + for (auto s = sockets.begin(); s != sockets.end(); ++s) { + shutdown(*s, SHUT_RDWR); + close(*s); + } + for (auto t = threads.begin(); t != threads.end(); ++t) + t->join(); - return 0; + return 0; }