diff --git a/core/CAPI.cpp b/core/CAPI.cpp index 72917a412..6b0d6b456 100644 --- a/core/CAPI.cpp +++ b/core/CAPI.cpp @@ -732,6 +732,16 @@ int ZT_Endpoint_fromString( return reinterpret_cast(ep)->fromString(str) ? ZT_RESULT_OK : ZT_RESULT_ERROR_BAD_PARAMETER; } +int ZT_Endpoint_fromBytes( + ZT_Endpoint *ep, + const void *bytes, + unsigned int len) +{ + if ((!ep) || (!bytes) || (!len)) + return ZT_RESULT_ERROR_BAD_PARAMETER; + return (reinterpret_cast(ep)->unmarshal(reinterpret_cast(bytes), (int)len) > 0) ? 0 : ZT_RESULT_ERROR_BAD_PARAMETER; +} + /********************************************************************************************************************/ char *ZT_Fingerprint_toString(const ZT_Fingerprint *fp, char *buf, int capacity) @@ -848,6 +858,20 @@ int ZT_InetAddress_lessThan(const ZT_InetAddress *a, const ZT_InetAddress *b) /********************************************************************************************************************/ +int ZT_Dictionary_parse(const void *const dict, const unsigned int len, void *const arg, void (*f)(void *, const char *, unsigned int, const void *, unsigned int)) +{ + ZeroTier::Dictionary d; + if (d.decode(dict, len)) { + for(ZeroTier::Dictionary::const_iterator i(d.begin());i!=d.end();++i) { + f(arg, i->first.c_str(), (unsigned int)i->first.length(), i->second.data(), (unsigned int)i->second.size()); + } + return 1; + } + return 0; +} + +/********************************************************************************************************************/ + uint64_t ZT_random() { return ZeroTier::Utils::random(); diff --git a/core/Trace.cpp b/core/Trace.cpp index 1abd10729..752ceeb48 100644 --- a/core/Trace.cpp +++ b/core/Trace.cpp @@ -24,7 +24,7 @@ namespace ZeroTier { Trace::Trace(const RuntimeEnvironment *renv) : RR(renv), - _f(0) + m_traceFlags(0) { } @@ -34,12 +34,12 @@ void Trace::unexpectedError( const char *message, ...) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_UNEXPECTED_ERROR); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_MESSAGE,message); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_UNEXPECTED_ERROR); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::append(buf, ZT_TRACE_FIELD_MESSAGE, message); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_resettingPathsInScope( @@ -51,18 +51,20 @@ void Trace::_resettingPathsInScope( const InetAddress &newExternal, const InetAddress::IpScope scope) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + if (reporter) + Dictionary::appendObject(buf, ZT_TRACE_FIELD_IDENTITY_FINGERPRINT, reporter.fingerprint()); if (from) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_ENDPOINT,Endpoint(from)); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT, Endpoint(from)); if (oldExternal) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_OLD_ENDPOINT,Endpoint(oldExternal)); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_OLD_ENDPOINT, Endpoint(oldExternal)); if (newExternal) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_NEW_ENDPOINT,Endpoint(newExternal)); - Dictionary::append(buf,ZT_TRACE_FIELD_RESET_ADDRESS_SCOPE,scope); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_NEW_ENDPOINT, Endpoint(newExternal)); + Dictionary::append(buf, ZT_TRACE_FIELD_RESET_ADDRESS_SCOPE, scope); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_tryingNewPath( @@ -75,18 +77,18 @@ void Trace::_tryingNewPath( const uint8_t triggeringPacketVerb, const Identity &triggeringPeer) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL1_TRYING_NEW_PATH); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH,trying.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL1_TRYING_NEW_PATH); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_IDENTITY_FINGERPRINT, trying.fingerprint()); if (triggerAddress) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT,Endpoint(triggerAddress)); - Dictionary::appendPacketId(buf,ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_ID,triggeringPacketId); - Dictionary::append(buf,ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_VERB,triggeringPacketVerb); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT, Endpoint(triggerAddress)); + Dictionary::appendPacketId(buf, ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_ID, triggeringPacketId); + Dictionary::append(buf, ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_VERB, triggeringPacketVerb); if (triggeringPeer) - Dictionary::append(buf,ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT_HASH,triggeringPeer.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT, triggeringPeer.fingerprint()); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_learnedNewPath( @@ -97,17 +99,17 @@ void Trace::_learnedNewPath( const InetAddress &physicalAddress, const InetAddress &replaced) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL1_LEARNED_NEW_PATH); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::appendPacketId(buf,ZT_TRACE_FIELD_PACKET_ID,packetId); - Dictionary::append(buf,ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH,peerIdentity.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL1_LEARNED_NEW_PATH); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::appendPacketId(buf, ZT_TRACE_FIELD_PACKET_ID, packetId); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_IDENTITY_FINGERPRINT, peerIdentity.fingerprint()); if (physicalAddress) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_ENDPOINT,Endpoint(physicalAddress)); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_ENDPOINT, Endpoint(physicalAddress)); if (replaced) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_OLD_ENDPOINT,Endpoint(replaced)); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_OLD_ENDPOINT, Endpoint(replaced)); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_incomingPacketDropped( @@ -121,19 +123,20 @@ void Trace::_incomingPacketDropped( const uint8_t verb, const ZT_TracePacketDropReason reason) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL1_INCOMING_PACKET_DROPPED); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::appendPacketId(buf,ZT_TRACE_FIELD_PACKET_ID,packetId); - Dictionary::append(buf,ZT_TRACE_FIELD_NETWORK_ID,networkId); - Dictionary::append(buf,ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH,peerIdentity.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL1_INCOMING_PACKET_DROPPED); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::appendPacketId(buf, ZT_TRACE_FIELD_PACKET_ID, packetId); + Dictionary::append(buf, ZT_TRACE_FIELD_NETWORK_ID, networkId); + if (peerIdentity) + Dictionary::appendObject(buf, ZT_TRACE_FIELD_IDENTITY_FINGERPRINT, peerIdentity.fingerprint()); if (physicalAddress) - Dictionary::append(buf,ZT_TRACE_FIELD_ENDPOINT,Endpoint(physicalAddress)); - Dictionary::append(buf,ZT_TRACE_FIELD_PACKET_HOPS,hops); - Dictionary::append(buf,ZT_TRACE_FIELD_PACKET_VERB,verb); - Dictionary::append(buf,ZT_TRACE_FIELD_REASON,reason); + Dictionary::append(buf, ZT_TRACE_FIELD_ENDPOINT, Endpoint(physicalAddress)); + Dictionary::append(buf, ZT_TRACE_FIELD_PACKET_HOPS, hops); + Dictionary::append(buf, ZT_TRACE_FIELD_PACKET_VERB, verb); + Dictionary::append(buf, ZT_TRACE_FIELD_REASON, reason); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_outgoingNetworkFrameDropped( @@ -147,18 +150,19 @@ void Trace::_outgoingNetworkFrameDropped( const uint8_t *frameData, const ZT_TraceFrameDropReason reason) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL1_INCOMING_PACKET_DROPPED); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_SOURCE_MAC,sourceMac.toInt()); - Dictionary::append(buf,ZT_TRACE_FIELD_DEST_MAC,destMac.toInt()); - Dictionary::append(buf,ZT_TRACE_FIELD_ETHERTYPE,etherType); - Dictionary::append(buf,ZT_TRACE_FIELD_FRAME_LENGTH,frameLength); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL1_INCOMING_PACKET_DROPPED); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::append(buf, ZT_TRACE_FIELD_NETWORK_ID, networkId); + Dictionary::append(buf, ZT_TRACE_FIELD_SOURCE_MAC, sourceMac.toInt()); + Dictionary::append(buf, ZT_TRACE_FIELD_DEST_MAC, destMac.toInt()); + Dictionary::append(buf, ZT_TRACE_FIELD_ETHERTYPE, etherType); + Dictionary::append(buf, ZT_TRACE_FIELD_FRAME_LENGTH, frameLength); if (frameData) - Dictionary::append(buf,ZT_TRACE_FIELD_FRAME_DATA,frameData,std::min((unsigned int)64,(unsigned int)frameLength)); - Dictionary::append(buf,ZT_TRACE_FIELD_REASON,reason); + Dictionary::append(buf, ZT_TRACE_FIELD_FRAME_DATA, frameData, std::min((unsigned int)64, (unsigned int)frameLength)); + Dictionary::append(buf, ZT_TRACE_FIELD_REASON, reason); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_incomingNetworkFrameDropped( @@ -167,6 +171,7 @@ void Trace::_incomingNetworkFrameDropped( const uint64_t networkId, const MAC &sourceMac, const MAC &destMac, + const uint16_t etherType, const Identity &peerIdentity, const InetAddress &physicalAddress, const uint8_t hops, @@ -176,23 +181,24 @@ void Trace::_incomingNetworkFrameDropped( const bool credentialRequestSent, const ZT_TraceFrameDropReason reason) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL2_INCOMING_FRAME_DROPPED); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_SOURCE_MAC,sourceMac.toInt()); - Dictionary::append(buf,ZT_TRACE_FIELD_DEST_MAC,destMac.toInt()); - Dictionary::append(buf,ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH,peerIdentity.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL2_INCOMING_FRAME_DROPPED); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::append(buf, ZT_TRACE_FIELD_SOURCE_MAC, sourceMac.toInt()); + Dictionary::append(buf, ZT_TRACE_FIELD_DEST_MAC, destMac.toInt()); + Dictionary::append(buf, ZT_TRACE_FIELD_ETHERTYPE, etherType); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_IDENTITY_FINGERPRINT, peerIdentity.fingerprint()); if (physicalAddress) - Dictionary::appendObject(buf,ZT_TRACE_FIELD_ENDPOINT,Endpoint(physicalAddress)); - Dictionary::append(buf,ZT_TRACE_FIELD_PACKET_HOPS,hops); - Dictionary::append(buf,ZT_TRACE_FIELD_PACKET_VERB,verb); - Dictionary::append(buf,ZT_TRACE_FIELD_FRAME_LENGTH,frameLength); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_ENDPOINT, Endpoint(physicalAddress)); + Dictionary::append(buf, ZT_TRACE_FIELD_PACKET_HOPS, hops); + Dictionary::append(buf, ZT_TRACE_FIELD_PACKET_VERB, verb); + Dictionary::append(buf, ZT_TRACE_FIELD_FRAME_LENGTH, frameLength); if (frameData) - Dictionary::append(buf,ZT_TRACE_FIELD_FRAME_DATA,frameData,std::min((unsigned int)64,(unsigned int)frameLength)); - Dictionary::append(buf,ZT_TRACE_FIELD_FLAG_CREDENTIAL_REQUEST_SENT,credentialRequestSent); - Dictionary::append(buf,ZT_TRACE_FIELD_REASON,reason); + Dictionary::append(buf, ZT_TRACE_FIELD_FRAME_DATA, frameData, std::min((unsigned int)64, (unsigned int)frameLength)); + Dictionary::append(buf, ZT_TRACE_FIELD_FLAG_CREDENTIAL_REQUEST_SENT, credentialRequestSent); + Dictionary::append(buf, ZT_TRACE_FIELD_REASON, reason); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_networkConfigRequestSent( @@ -200,12 +206,12 @@ void Trace::_networkConfigRequestSent( const uint32_t codeLocation, const uint64_t networkId) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_NETWORK_ID,networkId); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::append(buf, ZT_TRACE_FIELD_NETWORK_ID, networkId); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_networkFilter( @@ -228,30 +234,30 @@ void Trace::_networkFilter( const bool inbound, const int accept) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL2_NETWORK_FILTER); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_NETWORK_ID,networkId); - if ((primaryRuleSetLog)&&(!Utils::allZero(primaryRuleSetLog,512))) - Dictionary::append(buf,ZT_TRACE_FIELD_PRIMARY_RULE_SET_LOG,primaryRuleSetLog,512); - if ((matchingCapabilityRuleSetLog)&&(!Utils::allZero(matchingCapabilityRuleSetLog,512))) - Dictionary::append(buf,ZT_TRACE_FIELD_MATCHING_CAPABILITY_RULE_SET_LOG,matchingCapabilityRuleSetLog,512); - Dictionary::append(buf,ZT_TRACE_FIELD_MATCHING_CAPABILITY_ID,matchingCapabilityId); - Dictionary::append(buf,ZT_TRACE_FIELD_MATCHING_CAPABILITY_TIMESTAMP,matchingCapabilityTimestamp); - Dictionary::append(buf,ZT_TRACE_FIELD_SOURCE_ZT_ADDRESS,source); - Dictionary::append(buf,ZT_TRACE_FIELD_DEST_ZT_ADDRESS,dest); - Dictionary::append(buf,ZT_TRACE_FIELD_SOURCE_MAC,sourceMac.toInt()); - Dictionary::append(buf,ZT_TRACE_FIELD_DEST_MAC,destMac.toInt()); - Dictionary::append(buf,ZT_TRACE_FIELD_FRAME_LENGTH,frameLength); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL2_NETWORK_FILTER); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::append(buf, ZT_TRACE_FIELD_NETWORK_ID, networkId); + if ((primaryRuleSetLog) && (!Utils::allZero(primaryRuleSetLog, 512))) + Dictionary::append(buf, ZT_TRACE_FIELD_PRIMARY_RULE_SET_LOG, primaryRuleSetLog, 512); + if ((matchingCapabilityRuleSetLog) && (!Utils::allZero(matchingCapabilityRuleSetLog, 512))) + Dictionary::append(buf, ZT_TRACE_FIELD_MATCHING_CAPABILITY_RULE_SET_LOG, matchingCapabilityRuleSetLog, 512); + Dictionary::append(buf, ZT_TRACE_FIELD_MATCHING_CAPABILITY_ID, matchingCapabilityId); + Dictionary::append(buf, ZT_TRACE_FIELD_MATCHING_CAPABILITY_TIMESTAMP, matchingCapabilityTimestamp); + Dictionary::append(buf, ZT_TRACE_FIELD_SOURCE_ZT_ADDRESS, source); + Dictionary::append(buf, ZT_TRACE_FIELD_DEST_ZT_ADDRESS, dest); + Dictionary::append(buf, ZT_TRACE_FIELD_SOURCE_MAC, sourceMac.toInt()); + Dictionary::append(buf, ZT_TRACE_FIELD_DEST_MAC, destMac.toInt()); + Dictionary::append(buf, ZT_TRACE_FIELD_FRAME_LENGTH, frameLength); if (frameData) - Dictionary::append(buf,ZT_TRACE_FIELD_FRAME_DATA,frameData,std::min((unsigned int)64,(unsigned int)frameLength)); - Dictionary::append(buf,ZT_TRACE_FIELD_ETHERTYPE,etherType); - Dictionary::append(buf,ZT_TRACE_FIELD_VLAN_ID,vlanId); - Dictionary::append(buf,ZT_TRACE_FIELD_RULE_FLAG_NOTEE,noTee); - Dictionary::append(buf,ZT_TRACE_FIELD_RULE_FLAG_INBOUND,inbound); - Dictionary::append(buf,ZT_TRACE_FIELD_RULE_FLAG_ACCEPT,(int32_t)accept); + Dictionary::append(buf, ZT_TRACE_FIELD_FRAME_DATA, frameData, std::min((unsigned int)64, (unsigned int)frameLength)); + Dictionary::append(buf, ZT_TRACE_FIELD_ETHERTYPE, etherType); + Dictionary::append(buf, ZT_TRACE_FIELD_VLAN_ID, vlanId); + Dictionary::append(buf, ZT_TRACE_FIELD_RULE_FLAG_NOTEE, noTee); + Dictionary::append(buf, ZT_TRACE_FIELD_RULE_FLAG_INBOUND, inbound); + Dictionary::append(buf, ZT_TRACE_FIELD_RULE_FLAG_ACCEPT, (int32_t)accept); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } void Trace::_credentialRejected( @@ -264,17 +270,17 @@ void Trace::_credentialRejected( const uint8_t credentialType, const ZT_TraceCredentialRejectionReason reason) { - FCV buf; - Dictionary::append(buf,ZT_TRACE_FIELD_TYPE,ZT_TRACE_VL2_NETWORK_FILTER); - Dictionary::append(buf,ZT_TRACE_FIELD_CODE_LOCATION,codeLocation); - Dictionary::append(buf,ZT_TRACE_FIELD_NETWORK_ID,networkId); - Dictionary::append(buf,ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH,identity.fingerprint().hash,ZT_FINGERPRINT_HASH_SIZE); - Dictionary::append(buf,ZT_TRACE_FIELD_CREDENTIAL_ID,credentialId); - Dictionary::append(buf,ZT_TRACE_FIELD_CREDENTIAL_TIMESTAMP,credentialTimestamp); - Dictionary::append(buf,ZT_TRACE_FIELD_CREDENTIAL_TYPE,credentialType); - Dictionary::append(buf,ZT_TRACE_FIELD_REASON,reason); + FCV< uint8_t, 4096 > buf; + Dictionary::append(buf, ZT_TRACE_FIELD_TYPE, ZT_TRACE_VL2_NETWORK_CREDENTIAL_REJECTED); + Dictionary::append(buf, ZT_TRACE_FIELD_CODE_LOCATION, codeLocation); + Dictionary::append(buf, ZT_TRACE_FIELD_NETWORK_ID, networkId); + Dictionary::appendObject(buf, ZT_TRACE_FIELD_IDENTITY_FINGERPRINT, identity.fingerprint()); + Dictionary::append(buf, ZT_TRACE_FIELD_CREDENTIAL_ID, credentialId); + Dictionary::append(buf, ZT_TRACE_FIELD_CREDENTIAL_TIMESTAMP, credentialTimestamp); + Dictionary::append(buf, ZT_TRACE_FIELD_CREDENTIAL_TYPE, credentialType); + Dictionary::append(buf, ZT_TRACE_FIELD_REASON, reason); buf.push_back(0); - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,buf.data()); + RR->node->postEvent(tPtr, ZT_EVENT_TRACE, buf.data()); } } // namespace ZeroTier diff --git a/core/Trace.hpp b/core/Trace.hpp index c15644e55..08e231be7 100644 --- a/core/Trace.hpp +++ b/core/Trace.hpp @@ -31,15 +31,25 @@ namespace ZeroTier { class RuntimeEnvironment; + class Identity; + class Peer; + class Path; + class Network; + class MembershipCredential; + class OwnershipCredential; + class RevocationCredential; + class TagCredential; + class CapabilityCredential; + struct NetworkConfig; /** @@ -62,14 +72,16 @@ public: { uint8_t l[ZT_MAX_NETWORK_RULES / 2]; // ZT_MAX_NETWORK_RULES 4-bit fields - ZT_INLINE void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches) noexcept + ZT_INLINE void log(const unsigned int rn, const uint8_t thisRuleMatches, const uint8_t thisSetMatches) noexcept { - l[rn >> 1U] |= ( ((thisRuleMatches + 1U) << 2U) | (thisSetMatches + 1U) ) << ((rn & 1U) << 2U); + l[rn >> 1U] |= (((thisRuleMatches + 1U) << 2U) | (thisSetMatches + 1U)) << ((rn & 1U) << 2U); } - ZT_INLINE void logSkipped(const unsigned int rn,const uint8_t thisSetMatches) noexcept + + ZT_INLINE void logSkipped(const unsigned int rn, const uint8_t thisSetMatches) noexcept { l[rn >> 1U] |= (thisSetMatches + 1U) << ((rn & 1U) << 2U); } + ZT_INLINE void clear() noexcept { memoryZero(this); @@ -93,8 +105,8 @@ public: const InetAddress &newExternal, const InetAddress::IpScope scope) { - if ((_f & ZT_TRACE_F_VL1) != 0) - _resettingPathsInScope(tPtr,codeLocation,reporter,from,oldExternal,newExternal,scope); + if ((m_traceFlags & ZT_TRACE_F_VL1) != 0) + _resettingPathsInScope(tPtr, codeLocation, reporter, from, oldExternal, newExternal, scope); } ZT_INLINE void tryingNewPath( @@ -107,8 +119,8 @@ public: uint8_t triggeringPacketVerb, const Identity &triggeringPeer) { - if ((_f & ZT_TRACE_F_VL1) != 0) - _tryingNewPath(tPtr,codeLocation,trying,physicalAddress,triggerAddress,triggeringPacketId,triggeringPacketVerb,triggeringPeer); + if ((m_traceFlags & ZT_TRACE_F_VL1) != 0) + _tryingNewPath(tPtr, codeLocation, trying, physicalAddress, triggerAddress, triggeringPacketId, triggeringPacketVerb, triggeringPeer); } ZT_INLINE void learnedNewPath( @@ -119,8 +131,8 @@ public: const InetAddress &physicalAddress, const InetAddress &replaced) { - if ((_f & ZT_TRACE_F_VL1) != 0) - _learnedNewPath(tPtr,codeLocation,packetId,peerIdentity,physicalAddress,replaced); + if ((m_traceFlags & ZT_TRACE_F_VL1) != 0) + _learnedNewPath(tPtr, codeLocation, packetId, peerIdentity, physicalAddress, replaced); } ZT_INLINE void incomingPacketDropped( @@ -134,8 +146,8 @@ public: uint8_t verb, const ZT_TracePacketDropReason reason) { - if ((_f & ZT_TRACE_F_VL1) != 0) - _incomingPacketDropped(tPtr,codeLocation,packetId,networkId,peerIdentity,physicalAddress,hops,verb,reason); + if ((m_traceFlags & ZT_TRACE_F_VL1) != 0) + _incomingPacketDropped(tPtr, codeLocation, packetId, networkId, peerIdentity, physicalAddress, hops, verb, reason); } ZT_INLINE void outgoingNetworkFrameDropped( @@ -149,8 +161,8 @@ public: const uint8_t *frameData, ZT_TraceFrameDropReason reason) { - if ((_f & ZT_TRACE_F_VL2) != 0) - _outgoingNetworkFrameDropped(tPtr,codeLocation,networkId,sourceMac,destMac,etherType,frameLength,frameData,reason); + if ((m_traceFlags & ZT_TRACE_F_VL2) != 0) + _outgoingNetworkFrameDropped(tPtr, codeLocation, networkId, sourceMac, destMac, etherType, frameLength, frameData, reason); } ZT_INLINE void incomingNetworkFrameDropped( @@ -159,6 +171,7 @@ public: uint64_t networkId, const MAC &sourceMac, const MAC &destMac, + const uint16_t etherType, const Identity &peerIdentity, const InetAddress &physicalAddress, uint8_t hops, @@ -168,8 +181,8 @@ public: bool credentialRequestSent, ZT_TraceFrameDropReason reason) { - if ((_f & ZT_TRACE_F_VL2) != 0) - _incomingNetworkFrameDropped(tPtr,codeLocation,networkId,sourceMac,destMac,peerIdentity,physicalAddress,hops,frameLength,frameData,verb,credentialRequestSent,reason); + if ((m_traceFlags & ZT_TRACE_F_VL2) != 0) + _incomingNetworkFrameDropped(tPtr, codeLocation, networkId, sourceMac, destMac, etherType, peerIdentity, physicalAddress, hops, frameLength, frameData, verb, credentialRequestSent, reason); } ZT_INLINE void networkConfigRequestSent( @@ -177,8 +190,8 @@ public: const uint32_t codeLocation, uint64_t networkId) { - if ((_f & ZT_TRACE_F_VL2) != 0) - _networkConfigRequestSent(tPtr,codeLocation,networkId); + if ((m_traceFlags & ZT_TRACE_F_VL2) != 0) + _networkConfigRequestSent(tPtr, codeLocation, networkId); } ZT_INLINE void networkFilter( @@ -201,7 +214,7 @@ public: bool inbound, int accept) { - if ((_f & ZT_TRACE_F_VL2_FILTER) != 0) { + if ((m_traceFlags & ZT_TRACE_F_VL2_FILTER) != 0) { _networkFilter( tPtr, codeLocation, @@ -234,8 +247,8 @@ public: uint8_t credentialType, ZT_TraceCredentialRejectionReason reason) { - if ((_f & ZT_TRACE_F_VL2) != 0) - _credentialRejected(tPtr,codeLocation,networkId,identity,credentialId,credentialTimestamp,credentialType,reason); + if ((m_traceFlags & ZT_TRACE_F_VL2) != 0) + _credentialRejected(tPtr, codeLocation, networkId, identity, credentialId, credentialTimestamp, credentialType, reason); } private: @@ -247,6 +260,7 @@ private: const InetAddress &oldExternal, const InetAddress &newExternal, InetAddress::IpScope scope); + void _tryingNewPath( void *tPtr, uint32_t codeLocation, @@ -256,6 +270,7 @@ private: uint64_t triggeringPacketId, uint8_t triggeringPacketVerb, const Identity &triggeringPeer); + void _learnedNewPath( void *tPtr, uint32_t codeLocation, @@ -263,6 +278,7 @@ private: const Identity &peerIdentity, const InetAddress &physicalAddress, const InetAddress &replaced); + void _incomingPacketDropped( void *tPtr, uint32_t codeLocation, @@ -273,6 +289,7 @@ private: uint8_t hops, uint8_t verb, ZT_TracePacketDropReason reason); + void _outgoingNetworkFrameDropped( void *tPtr, uint32_t codeLocation, @@ -283,12 +300,14 @@ private: uint16_t frameLength, const uint8_t *frameData, ZT_TraceFrameDropReason reason); + void _incomingNetworkFrameDropped( void *tPtr, uint32_t codeLocation, uint64_t networkId, const MAC &sourceMac, const MAC &destMac, + const uint16_t etherType, const Identity &peerIdentity, const InetAddress &physicalAddress, uint8_t hops, @@ -297,10 +316,12 @@ private: uint8_t verb, bool credentialRequestSent, ZT_TraceFrameDropReason reason); + void _networkConfigRequestSent( void *tPtr, uint32_t codeLocation, uint64_t networkId); + void _networkFilter( void *tPtr, uint32_t codeLocation, @@ -320,6 +341,7 @@ private: bool noTee, bool inbound, int accept); + void _credentialRejected( void *tPtr, uint32_t codeLocation, @@ -331,7 +353,7 @@ private: ZT_TraceCredentialRejectionReason reason); const RuntimeEnvironment *const RR; - volatile unsigned int _f; // faster than atomic, but may not "instantly" change... will after next memory fence + volatile unsigned int m_traceFlags; // faster than atomic, but may not "instantly" change... should be okay }; } // namespace ZeroTier diff --git a/core/zerotier.h b/core/zerotier.h index 0ead8cdfd..f2fb07ee4 100644 --- a/core/zerotier.h +++ b/core/zerotier.h @@ -725,7 +725,8 @@ enum ZT_TraceEventType ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED = 100, ZT_TRACE_VL2_INCOMING_FRAME_DROPPED = 101, ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED = 102, - ZT_TRACE_VL2_NETWORK_FILTER = 103 + ZT_TRACE_VL2_NETWORK_FILTER = 103, + ZT_TRACE_VL2_NETWORK_CREDENTIAL_REJECTED = 104, }; /** @@ -778,10 +779,10 @@ enum ZT_TraceCredentialRejectionReason #define ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT "te" #define ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_ID "ti" #define ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_VERB "tv" -#define ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT_HASH "tp" +#define ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT "tp" #define ZT_TRACE_FIELD_MESSAGE "m" #define ZT_TRACE_FIELD_RESET_ADDRESS_SCOPE "rs" -#define ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH "f" +#define ZT_TRACE_FIELD_IDENTITY_FINGERPRINT "f" #define ZT_TRACE_FIELD_PACKET_ID "p" #define ZT_TRACE_FIELD_PACKET_VERB "v" #define ZT_TRACE_FIELD_PACKET_HOPS "h" @@ -933,9 +934,10 @@ enum ZT_Event * Trace (debugging) message * * These events are only generated if this is a TRACE-enabled build. - * This is for local debug traces, not remote trace diagnostics. + * This is for local debug traces, not remote trace diagnostics. The + * supplied Dictionary will always be null-terminated. * - * Meta-data: struct of type ZT_Trace_* + * Meta-data: null-terminated Dictionary containing trace info */ ZT_EVENT_TRACE = 5, @@ -2620,6 +2622,19 @@ ZT_SDK_API int ZT_Endpoint_fromString( ZT_Endpoint *ep, const char *str); +/** + * Decode a binary serialized endpoint + * + * @param ep Endpoint structure to populate + * @param bytes Bytes to decode + * @param len Length of bytes + * @return OK (0) or error code + */ +ZT_SDK_API int ZT_Endpoint_fromBytes( + ZT_Endpoint *ep, + const void *bytes, + unsigned int len); + /* ---------------------------------------------------------------------------------------------------------------- */ /** @@ -3021,6 +3036,24 @@ ZT_SDK_API const int ZT_AF_INET,ZT_AF_INET6; /* ---------------------------------------------------------------------------------------------------------------- */ +/** + * Parse a dictionary and invoke 'f' for each key/value pair. + * + * This can be used to parse a dictionary such as a network config or trace + * data supplied with a trace event. + * + * Function arguments are: arg, key, length of key (not including terminating null), + * value, length of value in bytes. + * + * @param dict Dictionary in serialized form + * @param len Maximum length of 'dict' (will also end at first zero) + * @param f Function to invoke with each key and (binary) value + * @return Non-zero if dictionary was valid + */ +ZT_SDK_API int ZT_Dictionary_parse(const void *dict, unsigned int len, void *arg, void (*f)(void *, const char *, unsigned int, const void *, unsigned int)); + +/* ---------------------------------------------------------------------------------------------------------------- */ + ZT_SDK_API uint64_t ZT_random(); #ifdef __cplusplus diff --git a/rust-zerotier-core/src/address.rs b/rust-zerotier-core/src/address.rs index 144a7fec1..4505d9253 100644 --- a/rust-zerotier-core/src/address.rs +++ b/rust-zerotier-core/src/address.rs @@ -15,6 +15,16 @@ use std::cmp::Ordering; pub struct Address(pub u64); +impl From<&[u8]> for Address { + fn from(bytes: &[u8]) -> Self { + if bytes.len() >= 5 { + Address(((bytes[0] as u64) << 32) | ((bytes[0] as u64) << 24) | ((bytes[0] as u64) << 16) | ((bytes[0] as u64) << 8) | (bytes[0] as u64)) + } else { + Address(0) + } + } +} + impl ToString for Address { fn to_string(&self) -> String { format!("{:0>10x}", self.0) @@ -23,14 +33,14 @@ impl ToString for Address { impl From for Address { #[inline(always)] - fn from(i: u64) -> Address { + fn from(i: u64) -> Self { Address(i) } } impl From<&str> for Address { #[inline(always)] - fn from(s: &str) -> Address { + fn from(s: &str) -> Self { Address(u64::from_str_radix(s, 16).unwrap_or(0)) } } diff --git a/rust-zerotier-core/src/dictionary.rs b/rust-zerotier-core/src/dictionary.rs new file mode 100644 index 000000000..2b5d4fec1 --- /dev/null +++ b/rust-zerotier-core/src/dictionary.rs @@ -0,0 +1,109 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2025-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +use std::collections::HashMap; +use std::ffi::c_void; +use std::os::raw::{c_char, c_uint}; + +use crate::{cstr_to_string, ResultCode}; +use crate::capi::ZT_Dictionary_parse; + +/// Rust interface to the Dictionary data structure. +pub struct Dictionary { + data: HashMap>, +} + +// Callback called by ZeroTier core to parse a Dictionary, populates a Rust Dictionary object. +extern "C" fn populate_dict_callback(arg: *mut c_void, c_key: *const c_char, key_len: c_uint, c_value: *const c_void, value_len: c_uint) { + unsafe { + let d = &mut *(arg.cast::()); + let k = cstr_to_string(c_key, key_len as isize); + if !k.is_empty() { + let mut v: Vec = Vec::new(); + if value_len > 0 { + let vp = c_value.cast::(); + v.reserve(value_len as usize); + for i in 0..(value_len as isize) { + v.push(*(vp.offset(i))); + } + } + let _ = d.data.insert(k, v); + } + } +} + +pub type DictionaryIter = std::collections::hash_map::Iter<'_, String, Vec>; + +impl Dictionary { + #[inline(always)] + pub fn new() -> Dictionary { + Dictionary { + data: HashMap::new(), + } + } + + pub fn new_from_bytes(dict: &[u8]) -> Result { + let mut d = Dictionary{ + data: HashMap::new(), + }; + unsafe { + if ZT_Dictionary_parse(dict.as_ptr().cast(), dict.len() as c_uint, (&mut d as *mut Dictionary).cast(), Some(populate_dict_callback)) != 0 { + Ok(d) + } else { + Err(ResultCode::ErrorBadParameter) + } + } + } + + pub fn get>(&self, k: K) -> Option<&Vec> { + let ks = String::from(String::from_utf8_lossy(k.as_ref())); + self.data.get(&ks) + } + + pub fn get_str>(&self, k: K) -> Option<&str> { + let v = self.data.get(k); + v.map_or(None, |v: Vec| { + let vs = std::str::from_utf8(v.as_slice()); + vs.map_or(None, |v: &str| { + Some(v) + }) + }) + } + + pub fn get_string_or_empty>(&self, k: K) -> String { + self.get_str(k).map_or_else(|| { String::new() },|s| { String::from(s) }) + } + + pub fn get_ui>(&self, k: K) -> Option { + let v = self.get_str(k); + v.map_or(None, |v: &str| { + let vi = u64::from_str_radix(v, 16); + vi.map_or(None, |i: u64| { + Some(i) + }) + }) + } + + #[inline(always)] + pub fn iter(&self) -> DictionaryIter { + self.data.iter() + } +} + +impl Clone for Dictionary { + fn clone(&self) -> Self { + Dictionary { + data: self.data.clone(), + } + } +} diff --git a/rust-zerotier-core/src/endpoint.rs b/rust-zerotier-core/src/endpoint.rs index 6ce5408de..6f7439ad0 100644 --- a/rust-zerotier-core/src/endpoint.rs +++ b/rust-zerotier-core/src/endpoint.rs @@ -13,7 +13,7 @@ use std::ffi::CString; use std::mem::MaybeUninit; -use std::os::raw::c_char; +use std::os::raw::{c_char, c_uint}; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::FromPrimitive; @@ -47,6 +47,21 @@ impl Endpoint { }; } + pub fn new_from_bytes(bytes: &[u8]) -> Result { + unsafe { + let mut cep: MaybeUninit = MaybeUninit::uninit(); + let ec = ztcore::ZT_Endpoint_fromBytes(bytes.as_ptr().cast(), bytes.len() as c_uint); + if ec == 0 { + let epi = cep.assume_init(); + return Ok(Endpoint{ + type_: EndpointType::from_i32(epi.type_ as i32).unwrap(), + capi: epi + }); + } + return Err(ResultCode::from_i32(ec).unwrap()); + } + } + pub fn new_from_string(s: &str) -> Result { let cs = CString::new(s); if cs.is_err() { diff --git a/rust-zerotier-core/src/fingerprint.rs b/rust-zerotier-core/src/fingerprint.rs index 03ba35ceb..af9ed8f76 100644 --- a/rust-zerotier-core/src/fingerprint.rs +++ b/rust-zerotier-core/src/fingerprint.rs @@ -17,6 +17,7 @@ use std::os::raw::{c_char, c_int}; use crate::*; use crate::capi as ztcore; +use std::ptr::copy_nonoverlapping; #[derive(PartialEq, Eq)] pub struct Fingerprint { @@ -51,6 +52,22 @@ impl Fingerprint { } return Err(ResultCode::ErrorBadParameter); } + + pub fn new_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < (5 + 48) { + let h: MaybeUninit<[u8; 48]> = MaybeUninit::uninit(); + let mut fp = Fingerprint { + address: Address::from(bytes), + hash: unsafe { h.assume_init() }, + }; + unsafe { + copy_nonoverlapping(bytes.as_ptr().offset(5), fp.hash.as_mut_ptr(), 48); + } + Ok(fp) + } else { + Err(ResultCode::ErrorBadParameter) + } + } } impl ToString for Fingerprint { diff --git a/rust-zerotier-core/src/lib.rs b/rust-zerotier-core/src/lib.rs index 6fb00c7c5..deda3e01e 100644 --- a/rust-zerotier-core/src/lib.rs +++ b/rust-zerotier-core/src/lib.rs @@ -33,6 +33,8 @@ mod buffer; mod portableatomici64; mod virtualnetworkconfig; mod multicastgroup; +mod dictionary; +pub mod trace; use crate::capi as ztcore; @@ -52,6 +54,7 @@ pub use buffer::Buffer; pub use portableatomici64::PortableAtomicI64; pub use virtualnetworkconfig::*; pub use multicastgroup::MulticastGroup; +pub use dictionary::*; /// Recommended minimum thread stack size for background threads. pub const RECOMMENDED_THREAD_STACK_SIZE: usize = 262144; @@ -105,52 +108,6 @@ pub enum CredentialType { Revocation = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_REVOCATION as isize, } -#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] -pub enum TraceEventType { - UnexpectedError = ztcore::ZT_TraceEventType_ZT_TRACE_UNEXPECTED_ERROR as isize, - ResetingPathsInScope = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE as isize, - TryingNewPath = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_TRYING_NEW_PATH as isize, - LearnedNewPath = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_LEARNED_NEW_PATH as isize, - IncomingPacketDropped = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_INCOMING_PACKET_DROPPED as isize, - OutgoingFrameDropped = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED as isize, - IncomingFrameDropped = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_INCOMING_FRAME_DROPPED as isize, - NetworkConfigRequested = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED as isize, - NetworkFilter = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_FILTER as isize, -} - -#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] -pub enum TracePacketDropReason { - Unspecified = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED as isize, - PeerTooOld = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_PEER_TOO_OLD as isize, - MalformedPacket = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET as isize, - MacFailed = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED as isize, - RateLimitExceeded = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED as isize, - InvalidObject = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT as isize, - InvalidCompressedData = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_INVALID_COMPRESSED_DATA as isize, - UnrecognizedVerb = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB as isize, - ReplyNotExpected = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_REPLY_NOT_EXPECTED as isize, -} - -#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] -pub enum TraceFrameDropReason { - Unspecified = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_UNSPECIFIED as isize, - BridgingNotAllowedRemote = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_REMOTE as isize, - BridgingNotAllowedLocal = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_LOCAL as isize, - MulticastDisabled = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_MULTICAST_DISABLED as isize, - BroadcastDisabled = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BROADCAST_DISABLED as isize, - FilterBlocked = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED as isize, - FilterBlockedAtBridgeReplication = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED_AT_BRIDGE_REPLICATION as isize, - PermissionDenied = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_PERMISSION_DENIED as isize, -} - -#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] -pub enum TraceCredentialRejectionReason { - SignatureVerificationFailed = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED as isize, - Revoked = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED as isize, - OlderThanLatest = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST as isize, - Invalid = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID as isize, -} - #[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] pub enum ResultCode { Ok = ztcore::ZT_ResultCode_ZT_RESULT_OK as isize, @@ -194,12 +151,6 @@ pub fn version() -> (i32, i32, i32, i32) { (major as i32, minor as i32, revision as i32, build as i32) } -/// Convenience function to get the number of milliseconds since the Unix epoch. -#[inline(always)] -pub fn now() -> i64 { - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64 -} - /// Get a random 64-bit integer using the non-cryptographic PRNG in the ZeroTier core. #[inline(always)] pub fn random() -> u64 { diff --git a/rust-zerotier-core/src/locator.rs b/rust-zerotier-core/src/locator.rs index 82057bae0..f9d5686aa 100644 --- a/rust-zerotier-core/src/locator.rs +++ b/rust-zerotier-core/src/locator.rs @@ -53,7 +53,7 @@ impl Locator { } } - pub fn endpoints(&self) -> Box<[Endpoint]> { + pub fn endpoints(&self) -> Vec { let mut eps: Vec = Vec::new(); unsafe { let ep_count = ztcore::ZT_Locator_endpointCount(self.capi) as usize; @@ -65,7 +65,7 @@ impl Locator { } } } - eps.into_boxed_slice() + eps } } diff --git a/rust-zerotier-core/src/node.rs b/rust-zerotier-core/src/node.rs index ffbb047bd..369016938 100644 --- a/rust-zerotier-core/src/node.rs +++ b/rust-zerotier-core/src/node.rs @@ -25,7 +25,7 @@ use crate::*; use crate::capi as ztcore; /// Maximum delay between calls to run_background_tasks() -pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: u64 = 250; +pub const NODE_BACKGROUND_TASKS_MAX_INTERVAL: u64 = 200; #[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] pub enum Event { @@ -316,9 +316,7 @@ extern "C" fn zt_path_lookup_function + Sync + Send + Clo impl + Sync + Send + Clone + 'static, N: 'static> Node { /// Create a new Node with a given event handler. - pub fn new(event_handler: T) -> Result, ResultCode> { - let now = now(); - + pub fn new(event_handler: T, now: i64) -> Result, ResultCode> { let mut n = Node { event_handler: event_handler.clone(), capi: null_mut(), @@ -354,15 +352,14 @@ impl + Sync + Send + Clone + 'static, N: 'static> Node u64 { - let current_time = now(); - self.now.set(current_time); + pub fn process_background_tasks(&self, now: i64) -> u64 { + self.now.set(now); - let mut next_task_deadline: i64 = current_time; + let mut next_task_deadline: i64 = now; unsafe { - ztcore::ZT_Node_processBackgroundTasks(self.capi, null_mut(), current_time, (&mut next_task_deadline as *mut i64).cast()); + ztcore::ZT_Node_processBackgroundTasks(self.capi, null_mut(), now, (&mut next_task_deadline as *mut i64).cast()); } - let mut next_delay = next_task_deadline - current_time; + let mut next_delay = next_task_deadline - now; if next_delay < 1 { next_delay = 1; diff --git a/rust-zerotier-core/src/trace.rs b/rust-zerotier-core/src/trace.rs new file mode 100644 index 000000000..057935f9d --- /dev/null +++ b/rust-zerotier-core/src/trace.rs @@ -0,0 +1,411 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2025-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +use std::str; + +use crate::{capi as ztcore, Dictionary, Endpoint, Fingerprint, IpScope, MAC, Address, CredentialType}; +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::FromPrimitive; +use crate::trace::TraceEvent::TryingNewPath; + +/* +// Used to construct String instances from constant strings in C. This assumes +// the string is valid UTF8 and may panic or crash otherwise. +fn string_from_static_array>(a: A) -> &'static str { + str::from_utf8(a.as_ref()).unwrap() +} + +lazy_static! { + pub static ref TRACE_FIELD_TYPE: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_TYPE); + pub static ref TRACE_FIELD_CODE_LOCATION: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_CODE_LOCATION); + pub static ref TRACE_FIELD_ENDPOINT: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_ENDPOINT); + pub static ref TRACE_FIELD_OLD_ENDPOINT: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_OLD_ENDPOINT); + pub static ref TRACE_FIELD_NEW_ENDPOINT: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_NEW_ENDPOINT); + pub static ref TRACE_FIELD_TRIGGER_FROM_ENDPOINT: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT); + pub static ref TRACE_FIELD_TRIGGER_FROM_PACKET_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_ID); + pub static ref TRACE_FIELD_TRIGGER_FROM_PACKET_VERB: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_VERB); + pub static ref TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT_HASH: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT_HASH); + pub static ref TRACE_FIELD_MESSAGE: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_MESSAGE); + pub static ref TRACE_FIELD_RESET_ADDRESS_SCOPE: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_RESET_ADDRESS_SCOPE); + pub static ref TRACE_FIELD_IDENTITY_FINGERPRINT_HASH: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT_HASH); + pub static ref TRACE_FIELD_PACKET_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_PACKET_ID); + pub static ref TRACE_FIELD_PACKET_VERB: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_PACKET_VERB); + pub static ref TRACE_FIELD_PACKET_HOPS: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_PACKET_HOPS); + pub static ref TRACE_FIELD_NETWORK_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_NETWORK_ID); + pub static ref TRACE_FIELD_REASON: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_REASON); + pub static ref TRACE_FIELD_SOURCE_MAC: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_SOURCE_MAC); + pub static ref TRACE_FIELD_DEST_MAC: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_DEST_MAC); + pub static ref TRACE_FIELD_ETHERTYPE: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_ETHERTYPE); + pub static ref TRACE_FIELD_VLAN_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_VLAN_ID); + pub static ref TRACE_FIELD_FRAME_LENGTH: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH); + pub static ref TRACE_FIELD_FRAME_DATA: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_FRAME_DATA); + pub static ref TRACE_FIELD_FLAG_CREDENTIAL_REQUEST_SENT: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_FLAG_CREDENTIAL_REQUEST_SENT); + pub static ref TRACE_FIELD_PRIMARY_RULE_SET_LOG: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_PRIMARY_RULE_SET_LOG); + pub static ref TRACE_FIELD_MATCHING_CAPABILITY_RULE_SET_LOG: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_RULE_SET_LOG); + pub static ref TRACE_FIELD_MATCHING_CAPABILITY_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_ID); + pub static ref TRACE_FIELD_MATCHING_CAPABILITY_TIMESTAMP: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_TIMESTAMP); + pub static ref TRACE_FIELD_SOURCE_ZT_ADDRESS: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_SOURCE_ZT_ADDRESS); + pub static ref TRACE_FIELD_DEST_ZT_ADDRESS: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_DEST_ZT_ADDRESS); + pub static ref TRACE_FIELD_MATCHING_CAPABILITY_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_ID); + pub static ref TRACE_FIELD_RULE_FLAG_NOTEE: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_RULE_FLAG_NOTEE); + pub static ref TRACE_FIELD_RULE_FLAG_INBOUND: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_RULE_FLAG_INBOUND); + pub static ref TRACE_FIELD_RULE_FLAG_ACCEPT: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_RULE_FLAG_ACCEPT); + pub static ref TRACE_FIELD_CREDENTIAL_ID: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_CREDENTIAL_ID); + pub static ref TRACE_FIELD_CREDENTIAL_TYPE: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_CREDENTIAL_TYPE); + pub static ref TRACE_FIELD_CREDENTIAL_TIMESTAMP: &'static str = string_from_static_array(ztcore::ZT_TRACE_FIELD_CREDENTIAL_TIMESTAMP); +} + */ + +/* +#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] +pub enum TraceEventType { + UnexpectedError = ztcore::ZT_TraceEventType_ZT_TRACE_UNEXPECTED_ERROR as isize, + ResetingPathsInScope = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE as isize, + TryingNewPath = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_TRYING_NEW_PATH as isize, + LearnedNewPath = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_LEARNED_NEW_PATH as isize, + IncomingPacketDropped = ztcore::ZT_TraceEventType_ZT_TRACE_VL1_INCOMING_PACKET_DROPPED as isize, + OutgoingFrameDropped = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED as isize, + IncomingFrameDropped = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_INCOMING_FRAME_DROPPED as isize, + NetworkConfigRequested = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED as isize, + NetworkFilter = ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_FILTER as isize, +} + +*/ + +#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] +pub enum TracePacketDropReason { + Unspecified = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED as isize, + PeerTooOld = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_PEER_TOO_OLD as isize, + MalformedPacket = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET as isize, + MacFailed = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED as isize, + RateLimitExceeded = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED as isize, + InvalidObject = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT as isize, + InvalidCompressedData = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_INVALID_COMPRESSED_DATA as isize, + UnrecognizedVerb = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB as isize, + ReplyNotExpected = ztcore::ZT_TracePacketDropReason_ZT_TRACE_PACKET_DROP_REASON_REPLY_NOT_EXPECTED as isize, +} + +#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] +pub enum TraceFrameDropReason { + Unspecified = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_UNSPECIFIED as isize, + BridgingNotAllowedRemote = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_REMOTE as isize, + BridgingNotAllowedLocal = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_LOCAL as isize, + MulticastDisabled = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_MULTICAST_DISABLED as isize, + BroadcastDisabled = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_BROADCAST_DISABLED as isize, + FilterBlocked = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED as isize, + FilterBlockedAtBridgeReplication = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED_AT_BRIDGE_REPLICATION as isize, + PermissionDenied = ztcore::ZT_TraceFrameDropReason_ZT_TRACE_FRAME_DROP_REASON_PERMISSION_DENIED as isize, +} + +#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq)] +pub enum TraceCredentialRejectionReason { + SignatureVerificationFailed = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED as isize, + Revoked = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED as isize, + OlderThanLatest = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST as isize, + Invalid = ztcore::ZT_TraceCredentialRejectionReason_ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID as isize, +} + +#[derive(PartialEq, Eq)] +pub enum TraceAccept { + Reject, + Accept, + SuperAccept, +} + +pub enum TraceEvent { + UnexpectedError { + code_location: u32, + message: String, + }, + ResetingPathsInScope { + code_location: u32, + reporter: Option, + reporter_endpoint: Option, + my_old_external: Option, + my_new_external: Option, + scope: IpScope, + }, + TryingNewPath { + code_location: u32, + trying: Fingerprint, + trigger_peer: Option, + trigger_packet_from: Option, + trigger_packet_id: u64, + trigger_packet_verb: i32, + }, + LearnedNewPath { + code_location: u32, + learned_from_packet_id: u64, + peer: Fingerprint, + new_address: Option, + replaced_address: Option, + }, + IncomingPacketDropped { + code_location: u32, + packet_id: u64, + network_id: u64, + peer: Option, + peer_address: Option, + hops: i32, + verb: i32, + reason: TracePacketDropReason, + }, + OutgoingFrameDropped { + code_location: u32, + network_id: u64, + source_mac: MAC, + dest_mac: MAC, + ethertype: u16, + frame_length: u32, + frame_data: Vec, + reason: TraceFrameDropReason, + }, + IncomingFrameDropped { + code_location: u32, + source_mac: MAC, + dest_mac: MAC, + ethertype: u16, + peer: Fingerprint, + peer_address: Option, + hops: i32, + verb: i32, + frame_length: u32, + frame_data: Vec, + credential_request_sent: bool, + reason: TraceFrameDropReason, + }, + NetworkConfigRequested { + code_location: u32, + network_id: u64, + }, + NetworkFilter { + code_location: u32, + network_id: u64, + primary_rule_set_log: Vec, + matching_capability_rule_set_log: Vec, + matching_capability_id: u32, + matching_capability_timestamp: i64, + source_address: Address, + dest_address: Address, + source_mac: MAC, + dest_mac: MAC, + frame_length: u32, + frame_data: Vec, + ethertype: u16, + vlan_id: u16, + rule_flag_notee: bool, + rule_flag_inbound: bool, + rule_flag_accept: TraceAccept, + }, + NetworkCredentialRejected { + code_location: u32, + network_id: u64, + from_peer: Fingerprint, + credential_id: u32, + credential_timestamp: i64, + credential_type: CredentialType, + reason: TraceCredentialRejectionReason, + }, +} + +fn trace_optional_endpoint(bytes: Option<&Vec>) -> Option { + bytes.map_or(None, |ep| { + Endpoint::new_from_bytes(ep.as_slice()).map_or(None, |ep| { + Some(ep) + }) + }) +} + +fn trace_optional_fingerprint(bytes: Option<&Vec>) -> Option { + bytes.map_or(None, |fp| { + Fingerprint::new_from_bytes(fp).map_or(None, |fp| { + Some(fp) + }) + }) +} + +impl TraceEvent { + pub fn parse_message(msg: &Dictionary) -> Option { + msg.get_ui(ztcore::ZT_TRACE_FIELD_TYPE).map_or(None, |mt: u64| -> Option { + let cl = msg.get_ui(ztcore::ZT_TRACE_FIELD_CODE_LOCATION).unwrap_or(0) as u32; + match mt as u32 { + ztcore::ZT_TraceEventType_ZT_TRACE_UNEXPECTED_ERROR => { + Some(TraceEvent::UnexpectedError { + code_location: cl, + message: msg.get_string_or_empty(ztcore::ZT_TRACE_FIELD_MESSAGE), + }) + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE => { + Some(TraceEvent::ResetingPathsInScope { + code_location: cl, + reporter: trace_optional_fingerprint(msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT)), + reporter_endpoint: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT)), + my_old_external: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_OLD_ENDPOINT)), + my_new_external: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_NEW_ENDPOINT)), + scope: IpScope::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_RESET_ADDRESS_SCOPE).unwrap_or(0) as i32).unwrap_or(IpScope::None), + }) + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL1_TRYING_NEW_PATH => { + let tf = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT); + if tf.is_some() { + let tf = Fingerprint::new_from_bytes(tf.unwrap().as_slice()).ok(); + if tf.is_some() { + return Some(TraceEvent::TryingNewPath { + code_location: cl, + trying: tf.unwrap(), + trigger_peer: trace_optional_fingerprint(msg.get(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PEER_FINGERPRINT)), + trigger_packet_from: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_ENDPOINT)), + trigger_packet_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_ID).unwrap_or(0), + trigger_packet_verb: msg.get_ui(ztcore::ZT_TRACE_FIELD_TRIGGER_FROM_PACKET_VERB).unwrap_or(0) as i32, + }); + } + } + None + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL1_LEARNED_NEW_PATH => { + let fp = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT); + if fp.is_some() { + let fp = Fingerprint::new_from_bytes(fp.unwrap().as_slice()).ok(); + if fp.is_some() { + return Some(TraceEvent::LearnedNewPath { + code_location: cl, + learned_from_packet_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_ID).unwrap_or(0), + peer: fp.unwrap(), + new_address: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT)), + replaced_address: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_OLD_ENDPOINT)), + }); + } + } + None + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL1_INCOMING_PACKET_DROPPED => { + Some(TraceEvent::IncomingPacketDropped { + code_location: cl, + packet_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_ID).unwrap_or(0), + network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0), + peer: trace_optional_fingerprint(msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT)), + peer_address: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT)), + hops: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_HOPS).unwrap_or(0) as i32, + verb: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_VERB).unwrap_or(0) as i32, + reason: TracePacketDropReason.from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TracePacketDropReason::Unspecified), + }) + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED => { + Some(TraceEvent::OutgoingFrameDropped { + code_location: ci, + network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0), + source_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_MAC).unwrap_or(0)), + dest_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_MAC).unwrap_or(0)), + ethertype: msg.get_ui(ztcore::ZT_TRACE_FIELD_ETHERTYPE).unwrap_or(0) as u16, + frame_length: msg.get_ui(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH).unwrap_or(0) as u32, + frame_data: msg.get(ztcore::ZT_TRACE_FIELD_FRAME_DATA).map_or_else(|| -> Vec { + Vec::new() + },|d: &Vec| -> Vec { + d.clone() + }), + reason: TraceFrameDropReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TraceFrameDropReason::Unspecified), + }) + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL2_INCOMING_FRAME_DROPPED => { + let fp = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT); + if fp.is_some() { + let fp = Fingerprint::new_from_bytes(fp.unwrap().as_slice()).ok(); + if fp.is_some() { + return Some(TraceEvent::IncomingFrameDropped { + code_location: cl, + source_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_MAC).unwrap_or(0)), + dest_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_MAC).unwrap_or(0)), + ethertype: msg.get_ui(ztcore::ZT_TRACE_FIELD_ETHERTYPE).unwrap_or(0) as u16, + peer: fp.unwrap(), + peer_address: trace_optional_endpoint(msg.get(ztcore::ZT_TRACE_FIELD_ENDPOINT)), + hops: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_HOPS).unwrap_or(0) as i32, + verb: msg.get_ui(ztcore::ZT_TRACE_FIELD_PACKET_VERB).unwrap_or(0) as i32, + frame_length: msg.get_ui(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH).unwrap_or(0) as u32, + frame_data: msg.get(ztcore::ZT_TRACE_FIELD_FRAME_DATA).map_or_else(|| -> Vec { + Vec::new() + },|d: &Vec| -> Vec { + d.clone() + }), + credential_request_sent: msg.get_ui(ztcore::ZT_TRACE_FIELD_FLAG_CREDENTIAL_REQUEST_SENT).unwrap_or(0) != 0, + reason: TraceFrameDropReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TraceFrameDropReason::Unspecified), + }) + } + } + None + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED => { + Some(TraceEvent::NetworkConfigRequested { + code_location: cl, + network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0), + }) + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_FILTER => { + Some(TraceEvent::NetworkFilter { + code_location: cl, + network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0), + primary_rule_set_log: msg.get(ztcore::ZT_TRACE_FIELD_PRIMARY_RULE_SET_LOG).map_or_else(|| { + Vec::new() + },|l| { + l.clone() + }), + matching_capability_rule_set_log: msg.get(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_RULE_SET_LOG).map_or_else(|| { + Vec::new() + },|l| { + l.clone() + }), + matching_capability_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_ID).unwrap_or(0) as u32, + matching_capability_timestamp: msg.get_ui(ztcore::ZT_TRACE_FIELD_MATCHING_CAPABILITY_TIMESTAMP).unwrap_or(0) as i64, + source_address: Address(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_ZT_ADDRESS).unwrap_or(0)), + dest_address: Address(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_ZT_ADDRESS).unwrap_or(0)), + source_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_SOURCE_MAC).unwrap_or(0)), + dest_mac: MAC(msg.get_ui(ztcore::ZT_TRACE_FIELD_DEST_MAC).unwrap_or(0)), + frame_length: msg.get_ui(ztcore::ZT_TRACE_FIELD_FRAME_LENGTH).unwrap_or(0) as u32, + frame_data: msg.get(ztcore::ZT_TRACE_FIELD_FRAME_DATA).map_or_else(|| -> Vec { + Vec::new() + },|d: &Vec| -> Vec { + d.clone() + }), + ethertype: msg.get_ui(ztcore::ZT_TRACE_FIELD_ETHERTYPE).unwrap_or(0) as u16, + vlan_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_VLAN_ID).unwrap_or(0) as u16, + rule_flag_notee: msg.get_ui(ztcore::ZT_TRACE_FIELD_RULE_FLAG_NOTEE).unwrap_or(0) != 0, + rule_flag_inbound: msg.get_ui(ztcore::ZT_TRACE_FIELD_RULE_FLAG_INBOUND).unwrap_or(0) != 0, + rule_flag_accept: match msg.get(ztcore::ZT_TRACE_FIELD_RULE_FLAG_ACCEPT).map_or_else(|| -> i32 { 0 as i32 },|a| -> i32 { i32::from_str_radix(str::from_utf8(a).unwrap_or("0"), 16).unwrap_or(0) }) { + 1 => { TraceAccept::Accept }, + 2 => { TraceAccept::SuperAccept }, + _ => { TraceAccept::Reject }, + } + }) + }, + ztcore::ZT_TraceEventType_ZT_TRACE_VL2_NETWORK_CREDENTIAL_REJECTED => { + let fp = msg.get(ztcore::ZT_TRACE_FIELD_IDENTITY_FINGERPRINT); + if fp.is_some() { + let fp = Fingerprint::new_from_bytes(fp.unwrap().as_slice()).ok(); + if fp.is_some() { + return Some(TraceEvent::NetworkCredentialRejected { + code_location: cl, + network_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_NETWORK_ID).unwrap_or(0), + from_peer: fp.unwrap(), + credential_id: msg.get_ui(ztcore::ZT_TRACE_FIELD_CREDENTIAL_ID).unwrap_or(0) as u32, + credential_timestamp: msg.get_ui(ztcore::ZT_TRACE_FIELD_CREDENTIAL_TIMESTAMP).unwrap_or(0) as i64, + credential_type: CredentialType::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_CREDENTIAL_TYPE).unwrap_or(0) as i32).unwrap_or(CredentialType::Null), + reason: TraceCredentialRejectionReason::from_i32(msg.get_ui(ztcore::ZT_TRACE_FIELD_REASON).unwrap_or(0) as i32).unwrap_or(TraceCredentialRejectionReason::Invalid), + }); + } + } + None + }, + _ => None, + } + }) + } +} diff --git a/rust-zerotier-service/src/localconfig.rs b/rust-zerotier-service/src/localconfig.rs index 8e43745df..d763465cc 100644 --- a/rust-zerotier-service/src/localconfig.rs +++ b/rust-zerotier-service/src/localconfig.rs @@ -112,8 +112,8 @@ pub struct LocalConfigSettings { pub log_filter_events: bool, #[serde(rename = "logMulticastEvents")] pub log_multicast_events: bool, - #[serde(rename = "logDebug")] - pub log_debug: bool, + #[serde(rename = "logToStderr")] + pub log_to_stderr: bool, #[serde(rename = "interfacePrefixBlacklist")] pub interface_prefix_blacklist: Vec, #[serde(rename = "explicitAddresses")] @@ -197,7 +197,7 @@ impl Default for LocalConfigSettings { log_vl2_events: false, log_filter_events: false, log_multicast_events: false, - log_debug: false, + log_to_stderr: true, // TODO: change for release interface_prefix_blacklist: bl, explicit_addresses: Vec::new() } diff --git a/rust-zerotier-service/src/log.rs b/rust-zerotier-service/src/log.rs index a0199aeed..42938a680 100644 --- a/rust-zerotier-service/src/log.rs +++ b/rust-zerotier-service/src/log.rs @@ -14,7 +14,7 @@ use std::cell::Cell; use std::fmt::Display; use std::fs::{File, OpenOptions}; -use std::io::{Seek, SeekFrom, Write}; +use std::io::{Seek, SeekFrom, Write, stderr}; use std::sync::Mutex; use chrono::Datelike; @@ -23,18 +23,19 @@ struct LogIntl { file: Option, cur_size: u64, max_size: usize, + log_to_stderr: bool, } pub(crate) struct Log { prefix: String, path: String, - intl: Mutex, + inner: Mutex, } impl Log { const MIN_MAX_SIZE: usize = 1024; - pub fn new(path: &str, max_size: usize, prefix: &str) -> Log { + pub fn new(path: &str, max_size: usize, log_to_stderr: bool, prefix: &str) -> Log { let mut p = String::from(prefix); if !p.is_empty() { p.push(' '); @@ -42,25 +43,33 @@ impl Log { Log{ prefix: p, path: String::from(path), - intl: Mutex::new(LogIntl { + inner: Mutex::new(LogIntl { file: None, cur_size: 0, max_size: if max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { max_size }, + log_to_stderr: log_to_stderr, }), } } pub fn set_max_size(&self, new_max_size: usize) { - self.intl.lock().unwrap().max_size = if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size }; + self.inner.lock().unwrap().max_size = if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size }; + } + + pub fn set_log_to_stderr(&self, log_to_stderr: bool) { + self.inner.lock().unwrap().log_to_stderr = log_to_stderr; } pub fn log>(&self, s: S) { + // Output FATAL errors to stderr. let ss: &str = s.as_ref(); if ss.starts_with("FATAL") { eprintln!("{}", ss); } - let mut l = self.intl.lock().unwrap(); + let mut l = self.inner.lock().unwrap(); + + // If the file isn't open, open or create and seek to end. if l.file.is_none() { let mut f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str()); if f.is_err() { @@ -75,6 +84,7 @@ impl Log { l.file = Some(f); } + // If there is a maximum size limit configured, rotate if exceeded. if l.max_size > 0 && l.cur_size > l.max_size as u64 { l.file = None; l.cur_size = 0; @@ -92,12 +102,19 @@ impl Log { l.file = Some(f.unwrap()); } + let log_line = format!("{}[{}] {}\n", self.prefix.as_str(), chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), ss); + if l.log_to_stderr { + stderr().write_all(log_line.as_bytes()); + } let f = l.file.as_mut().unwrap(); - let now_str = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); - let log_line = format!("{}[{}] {}\n", self.prefix.as_str(), now_str.as_str(), ss); - let _ = f.write_all(log_line.as_bytes()); - let _ = f.flush(); - l.cur_size += log_line.len() as u64; + let e = f.write_all(log_line.as_bytes()); + if e.is_err() { + eprintln!("ERROR: I/O error writing to log: {}", e.err().unwrap().to_string()); + l.file = None; + } else { + let _ = f.flush(); + l.cur_size += log_line.len() as u64; + } } } diff --git a/rust-zerotier-service/src/main.rs b/rust-zerotier-service/src/main.rs index b29d50bae..2d89ffc5d 100644 --- a/rust-zerotier-service/src/main.rs +++ b/rust-zerotier-service/src/main.rs @@ -26,37 +26,60 @@ mod service; #[allow(non_snake_case,non_upper_case_globals,non_camel_case_types,dead_code,improper_ctypes)] mod osdep; // bindgen generated -use std::boxed::Box; use std::ffi::CStr; use std::path::Path; +use std::boxed::Box; use std::sync::Arc; +use std::mem::MaybeUninit; +use std::os::raw::c_uint; use crate::store::Store; +#[inline(always)] +pub(crate) fn sha512>(data: T) -> [u8; 64] { + unsafe { + let mut r: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); + let d = data.as_ref(); + osdep::sha512(d.as_ptr().cast(), d.len() as c_uint, r.as_mut_ptr().cast()); + r.assume_init() + } +} + +#[inline(always)] +pub(crate) fn sha384>(data: T) -> [u8; 48] { + unsafe { + let mut r: MaybeUninit<[u8; 48]> = MaybeUninit::uninit(); + let d = data.as_ref(); + osdep::sha384(d.as_ptr().cast(), d.len() as c_uint, r.as_mut_ptr().cast()); + r.assume_init() + } +} + +#[inline(always)] +pub(crate) fn ms_since_epoch() -> i64 { + // This is easy to do in the Rust stdlib, but the version in OSUtils is probably faster. + unsafe { osdep::msSinceEpoch() } +} + fn main() { let mut process_exit_value: i32 = 0; - let mut cli_args = Some(Box::new(cli::parse_cli_args())); + let cli_args = Box::new(cli::parse_cli_args()); let mut zerotier_path = unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), -1) }; - - let json_output: bool; let mut auth_token: Option = None; let mut auth_token_path: Option = None; - { - let a = cli_args.as_ref().unwrap(); - json_output = a.is_present("json"); - let v = a.value_of("path"); - if v.is_some() { - zerotier_path = String::from(v.unwrap()); - } - let v = a.value_of("token"); - if v.is_some() { - auth_token = Some(v.unwrap().trim().to_string()); - } - let v = a.value_of("token_path"); - if v.is_some() { - auth_token_path = Some(v.unwrap().to_string()); - } + //let json_output = cli_args.is_present("json"); + let v = cli_args.value_of("path"); + if v.is_some() { + zerotier_path = String::from(v.unwrap()); + } + let v = cli_args.value_of("token"); + if v.is_some() { + auth_token = Some(v.unwrap().trim().to_string()); + } + let v = cli_args.value_of("token_path"); + if v.is_some() { + auth_token_path = Some(v.unwrap().to_string()); } let store = Store::new(zerotier_path.as_str()); @@ -65,6 +88,13 @@ fn main() { std::process::exit(1); } let store = Arc::new(store.unwrap()); + if store.write_pid().is_err() { + eprintln!("FATAL: error writing to directory '{}': unable to write zerotier.pid", zerotier_path); + std::process::exit(1); + } + + // From this point on we shouldn't call std::process::exit() since that would + // fail to erase zerotier.pid from the working directory. if auth_token.is_none() { let t; @@ -80,17 +110,21 @@ fn main() { auth_token = Some(auth_token.unwrap().trim().to_string()); } + drop(zerotier_path); + drop(auth_token_path); + match cli_args.as_ref().unwrap().subcommand_name().unwrap() { "version" => { let ver = zerotier_core::version(); println!("{}.{}.{}", ver.0, ver.1, ver.2); }, "service" => { - cli_args = None; // free any memory we can when launching service + drop(cli_args); // free unnecssary memory before launching service process_exit_value = service::run(&store, auth_token); }, _ => cli::print_help(), // includes "help" } + store.erase_pid(); std::process::exit(process_exit_value); } diff --git a/rust-zerotier-service/src/service.rs b/rust-zerotier-service/src/service.rs index 9c8bbc913..39df463f7 100644 --- a/rust-zerotier-service/src/service.rs +++ b/rust-zerotier-service/src/service.rs @@ -23,10 +23,10 @@ use warp::{Filter, Reply}; use warp::http::{HeaderMap, Method, StatusCode}; use warp::hyper::body::Bytes; -use zerotier_core::{Buffer, Address, IpScope, Node, NodeEventHandler, NetworkId, VirtualNetworkConfigOperation, VirtualNetworkConfig, StateObjectType, MAC, Event, InetAddress, InetAddressFamily, Identity}; +use zerotier_core::{Buffer, Address, IpScope, Node, NodeEventHandler, NetworkId, VirtualNetworkConfigOperation, VirtualNetworkConfig, StateObjectType, MAC, Event, InetAddress, InetAddressFamily, Identity, Dictionary}; use crate::fastudpsocket::*; -use crate::getifaddrs; +use crate::{getifaddrs, ms_since_epoch}; use crate::localconfig::*; use crate::log::Log; use crate::network::Network; @@ -40,8 +40,9 @@ struct Service { log: Arc, _local_config: Arc>>, run: Arc, + online: Arc, store: Arc, - node: Weak>, // weak since Node can hold a reference to this + node: Weak>, // weak since Node itself may hold a reference to this } impl NodeEventHandler for Service { @@ -53,10 +54,21 @@ impl NodeEventHandler for Service { fn event(&self, event: Event, event_data: &[u8]) { match event { Event::Up => {} - Event::Down => {} - Event::Online => {} - Event::Offline => {} - Event::Trace => {} + Event::Down => { + self.run.store(false, Ordering::Relaxed); + } + Event::Online => { + self.online.store(true, Ordering::Relaxed); + } + Event::Offline => { + self.online.store(true, Ordering::Relaxed); + } + Event::Trace => { + if !event_data.is_empty() { + let _ = Dictionary::new_from_bytes(event_data).map(|tm| { + }); + } + } Event::UserMessage => {} } } @@ -126,21 +138,23 @@ impl Service { pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { let mut process_exit_value: i32 = 0; - let init_local_config = Arc::new(store.read_local_conf(false).unwrap_or(LocalConfig::default())); + let init_local_config = Arc::new(store.read_local_conf(false).unwrap_or_else(|_| { LocalConfig::default() })); - // Open log in store. let log = Arc::new(Log::new( - if init_local_config.settings.log_path.as_ref().is_some() { init_local_config.settings.log_path.as_ref().unwrap().as_str() } else { store.default_log_path.to_str().unwrap() }, + if init_local_config.settings.log_path.as_ref().is_some() { + init_local_config.settings.log_path.as_ref().unwrap().as_str() + } else { + store.default_log_path.to_str().unwrap() + }, init_local_config.settings.log_size_max, + init_local_config.settings.log_to_stderr, "", )); // Generate authtoken.secret from secure random bytes if not already set. let auth_token = auth_token.unwrap_or_else(|| { let mut rb = [0_u8; 64]; - unsafe { - crate::osdep::getSecureRandom(rb.as_mut_ptr().cast(), 64); - } + unsafe { crate::osdep::getSecureRandom(rb.as_mut_ptr().cast(), 64) }; let mut t = String::new(); t.reserve(64); for b in rb.iter() { @@ -172,12 +186,13 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { log: log.clone(), _local_config: Arc::new(Mutex::new(init_local_config)), run: Arc::new(AtomicBool::new(true)), + online: Arc::new(AtomicBool::new(false)), store: store.clone(), node: Weak::new(), }; // Create instance of Node which will call Service on events. - let node = Node::new(service.clone()); + let node = Node::new(service.clone(), ms_since_epoch()); if node.is_err() { process_exit_value = 1; l!(log, "FATAL: error initializing node: {}", node.err().unwrap().to_string()); @@ -188,7 +203,6 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { service.node = Arc::downgrade(&node); let service = service; // make immutable after setting node - let mut last_checked_config: i64 = 0; let mut loop_delay = zerotier_core::NODE_BACKGROUND_TASKS_MAX_INTERVAL; loop { let mut local_config = service.local_config(); @@ -221,17 +235,32 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { ); } if warp_server.is_err() { - l!(log, "ERROR: local API http server failed to bind to port {}: {}", local_config.settings.primary_port, warp_server.err().unwrap().to_string()); + l!(log, "ERROR: local API http server failed to bind to port {} or failed to start: {}", local_config.settings.primary_port, warp_server.err().unwrap().to_string()); break; } let warp_server = tokio_rt.spawn(warp_server.unwrap().1); + // Write zerotier.port which is used by the CLI to know how to reach the HTTP API. + store.write_port(local_config.settings.primary_port); + + let mut last_checked_config: i64 = 0; loop { + let loop_start = ms_since_epoch(); + let mut now: i64 = 0; + // Wait for (1) loop delay elapsed, (2) a signal to interrupt delay now, or // (3) an external signal to exit. tokio::select! { - _ = tokio::time::sleep(Duration::from_millis(loop_delay)) => {}, - _ = interrupt_rx.next() => {}, + _ = tokio::time::sleep(Duration::from_millis(loop_delay)) => { + now = ms_since_epoch(); + let actual_delay = now - loop_start; + if actual_delay > (loop_delay * 4) { + // TODO: handle likely sleep/wake or other system interruption + } + }, + _ = interrupt_rx.next() => { + now = ms_since_epoch(); + }, _ = tokio::signal::ctrl_c() => { l!(log, "exit signal received, shutting down..."); service.run.store(false, Ordering::Relaxed); @@ -241,7 +270,6 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { // Check every CONFIG_CHECK_INTERVAL for changes to either the system configuration // or the node's local configuration and take actions as needed. - let now = zerotier_core::now(); if (now - last_checked_config) >= CONFIG_CHECK_INTERVAL { last_checked_config = now; @@ -251,12 +279,17 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { service.set_local_config(new_config.unwrap()); } - // Check for configuration changes that require a reboot of the inner loop - // or other actions to be taken. + // Check for and handle configuration changes, some of which require inner loop restart. let next_local_config = service.local_config(); if local_config.settings.primary_port != next_local_config.settings.primary_port { break; } + if local_config.settings.log_size_max != next_local_config.settings.log_size_max { + log.set_max_size(next_local_config.settings.log_size_max); + } + if local_config.settings.log_to_stderr != next_local_config.settings.log_to_stderr { + log.set_log_to_stderr(next_local_config.settings.log_to_stderr); + } local_config = next_local_config; // Enumerate all useful addresses bound to interfaces on the system. @@ -320,17 +353,22 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { } } } + if primary_port_bind_failure { if local_config.settings.auto_port_search { - // TODO: port hunting if enabled + // TODO: port hunting } else { l!(log, "primary port {} failed to bind, waiting and trying again...", local_config.settings.primary_port); break; } } + if secondary_port_bind_failure { - l!(log, "secondary port {} failed to bind (non-fatal, will try again)", local_config.settings.secondary_port.unwrap_or(0)); - // hunt for a secondary port. + if local_config.settings.auto_port_search { + // TODO: port hunting + } else { + l!(log, "secondary port {} failed to bind (non-fatal, will try again)", local_config.settings.secondary_port.unwrap_or(0)); + } } } @@ -340,7 +378,7 @@ pub(crate) fn run(store: &Arc, auth_token: Option) -> i32 { } // Run background task handler in ZeroTier core. - loop_delay = node.process_background_tasks(); + loop_delay = node.process_background_tasks(now); } // Gracefully shut down the local web server. diff --git a/rust-zerotier-service/src/store.rs b/rust-zerotier-service/src/store.rs index 61636271a..05ecde8e8 100644 --- a/rust-zerotier-service/src/store.rs +++ b/rust-zerotier-service/src/store.rs @@ -15,12 +15,14 @@ use std::error::Error; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::sync::Mutex; +use std::str::FromStr; use std::ffi::CString; use zerotier_core::{StateObjectType, NetworkId}; use crate::localconfig::LocalConfig; +/// In-filesystem data store for configuration and objects. pub(crate) struct Store { pub base_path: Box, pub default_log_path: Box, @@ -195,6 +197,21 @@ impl Store { self.write_file("local.conf", json.as_bytes()) } + /// Writes the primary port number bound to zerotier.port. + pub fn write_port(&self, port: u16) -> std::io::Result<()> { + let ps = port.to_string(); + self.write_file("zerotier.port", ps.as_bytes()) + } + + /// Read zerotier.port and return port or 0 if not found or not readable. + pub fn read_port(&self) -> u16 { + self.read_file_str("zerotier.port").map_or_else(|_| { + 0_u16 + },|s| { + u16::from_str(s.trim()).unwrap_or(0_u16) + }) + } + /// Reads the authtoken.secret file in the home directory. #[inline(always)] pub fn read_authtoken_secret(&self) -> std::io::Result { @@ -209,6 +226,18 @@ impl Store { Ok(()) } + /// Write zerotier.pid file with current process's PID. + #[cfg(unix)] + pub fn write_pid(&self) -> std::io::Result<()> { + let pid = unsafe { crate::osdep::getpid() }.to_string(); + self.write_file(self.base_path.join("zerotier.pid").to_str().unwrap(), pid.as_bytes()) + } + + /// Erase zerotier.pid if present. + pub fn erase_pid(&self) { + std::fs::remove_file(self.base_path.join("zerotier.pid")); + } + /// Load a ZeroTier core object. pub fn load_object(&self, obj_type: &StateObjectType, obj_id: &[u64]) -> std::io::Result> { let obj_path = self.make_obj_path(&obj_type, obj_id); diff --git a/rust-zerotier-service/src/vnic/common.rs b/rust-zerotier-service/src/vnic/common.rs index 593a3bb73..ae6d12ed2 100644 --- a/rust-zerotier-service/src/vnic/common.rs +++ b/rust-zerotier-service/src/vnic/common.rs @@ -12,15 +12,17 @@ /****/ use std::collections::BTreeSet; -use std::ptr::null_mut; use std::os::raw::c_ulong; +use std::ptr::null_mut; + use zerotier_core::{MAC, MulticastGroup}; + use crate::osdep as osdep; #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "ios"))] pub(crate) fn bsd_get_multicast_groups(dev: &str) -> BTreeSet { - let dev = dev.as_bytes(); let mut groups: BTreeSet = BTreeSet::new(); + let dev = dev.as_bytes(); unsafe { let mut maddrs: *mut osdep::ifmaddrs = null_mut(); if osdep::getifmaddrs(&mut maddrs as *mut *mut osdep::ifmaddrs) == 0 { @@ -32,14 +34,7 @@ pub(crate) fn bsd_get_multicast_groups(dev: &str) -> BTreeSet { if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len() as osdep::u_char && osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen as c_ulong) == 0 { let mi = la.sdl_nlen as usize; groups.insert(MulticastGroup{ - mac: MAC( - (la.sdl_data[mi] as u64) << 40 | - (la.sdl_data[mi+1] as u64) << 32 | - (la.sdl_data[mi+2] as u64) << 24 | - (la.sdl_data[mi+3] as u64) << 16 | - (la.sdl_data[mi+4] as u64) << 8 | - la.sdl_data[mi+5] as u64 - ), + mac: MAC((la.sdl_data[mi] as u64) << 40 | (la.sdl_data[mi+1] as u64) << 32 | (la.sdl_data[mi+2] as u64) << 24 | (la.sdl_data[mi+3] as u64) << 16 | (la.sdl_data[mi+4] as u64) << 8 | la.sdl_data[mi+5] as u64), adi: 0, }); } @@ -51,3 +46,9 @@ pub(crate) fn bsd_get_multicast_groups(dev: &str) -> BTreeSet { } groups } + +#[cfg(target_os = "linux")] +pub(crate) fn linux_get_multicast_groups(dev: &str) -> BTreeSet { + let mut groups: BTreeSet = BTreeSet::new(); + groups +}