Outgoing side of packet counter for link quality reporting. Also some cleanup and a cluster mode build fix.

This commit is contained in:
Adam Ierymenko 2017-03-01 10:22:57 -08:00
parent 127bcb02ff
commit 2bf9145ae6
10 changed files with 974 additions and 967 deletions

View File

@ -79,8 +79,7 @@ public:
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
Buffer()
throw() :
Buffer() :
_l(0)
{
}
@ -419,87 +418,70 @@ public:
/**
* Set buffer data length to zero
*/
inline void clear()
throw()
{
_l = 0;
}
inline void clear() { _l = 0; }
/**
* Zero buffer up to size()
*/
inline void zero()
throw()
{
memset(_b,0,_l);
}
inline void zero() { memset(_b,0,_l); }
/**
* Zero unused capacity area
*/
inline void zeroUnused()
throw()
{
memset(_b + _l,0,C - _l);
}
inline void zeroUnused() { memset(_b + _l,0,C - _l); }
/**
* Unconditionally and securely zero buffer's underlying memory
*/
inline void burn()
throw()
{
Utils::burn(_b,sizeof(_b));
}
inline void burn() { Utils::burn(_b,sizeof(_b)); }
/**
* @return Constant pointer to data in buffer
*/
inline const void *data() const throw() { return _b; }
inline const void *data() const { return _b; }
/**
* @return Non-constant pointer to data in buffer
*/
inline void *unsafeData() { return _b; }
/**
* @return Size of data in buffer
*/
inline unsigned int size() const throw() { return _l; }
inline unsigned int size() const { return _l; }
/**
* @return Capacity of buffer
*/
inline unsigned int capacity() const throw() { return C; }
inline unsigned int capacity() const { return C; }
template<unsigned int C2>
inline bool operator==(const Buffer<C2> &b) const
throw()
{
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
}
template<unsigned int C2>
inline bool operator!=(const Buffer<C2> &b) const
throw()
{
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
}
template<unsigned int C2>
inline bool operator<(const Buffer<C2> &b) const
throw()
{
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
}
template<unsigned int C2>
inline bool operator>(const Buffer<C2> &b) const
throw()
{
return (b < *this);
}
template<unsigned int C2>
inline bool operator<=(const Buffer<C2> &b) const
throw()
{
return !(b < *this);
}
template<unsigned int C2>
inline bool operator>=(const Buffer<C2> &b) const
throw()
{
return !(*this < b);
}

View File

@ -255,7 +255,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
char polykey[ZT_POLY1305_KEY_LEN];
memset(polykey,0,sizeof(polykey));
s20.encrypt12(polykey,polykey,sizeof(polykey));
s20.crypt12(polykey,polykey,sizeof(polykey));
// Compute 16-byte MAC
char mac[ZT_POLY1305_MAC_LEN];
@ -267,7 +267,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
// Decrypt!
dmsg.setSize(len - 24);
s20.decrypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
s20.crypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
}
if (dmsg.size() < 4)
@ -954,10 +954,10 @@ void Cluster::_flush(uint16_t memberId)
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
char polykey[ZT_POLY1305_KEY_LEN];
memset(polykey,0,sizeof(polykey));
s20.encrypt12(polykey,polykey,sizeof(polykey));
s20.crypt12(polykey,polykey,sizeof(polykey));
// Encrypt m.q in place
s20.encrypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
s20.crypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
// Add MAC for authentication (encrypt-then-MAC)
char mac[ZT_POLY1305_MAC_LEN];

View File

@ -243,7 +243,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
outp.append((uint8_t)Packet::VERB_HELLO);
outp.append((uint64_t)pid);
outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
outp.armor(key,true);
outp.armor(key,true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
} else {
TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
@ -405,7 +405,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
RR->topology->appendCertificateOfRepresentation(outp);
outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),now);
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
@ -584,7 +584,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
}
if (count > 0) {
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
@ -610,7 +610,7 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<
const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
if (RR->node->shouldUsePathForZeroTierTraffic(with,_path->localAddress(),atAddr)) {
RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now(),false);
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now(),false,0);
TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
} else {
TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
@ -732,7 +732,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
outp.append((uint8_t)Packet::VERB_EXT_FRAME);
outp.append((uint64_t)packetId());
outp.append((uint64_t)nwid);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
@ -762,7 +762,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
outp.append((uint64_t)pid);
if (size() > ZT_PACKET_IDX_PAYLOAD)
outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
@ -957,7 +957,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
outp.append(requestPacketId);
outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
outp.append(nwid);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
@ -984,7 +984,7 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const Shared
outp.append((uint64_t)packetId());
outp.append((uint64_t)network->id());
outp.append((uint64_t)configUpdateId);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
}
@ -1033,7 +1033,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
outp.append((uint32_t)mg.adi());
const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
if (gatheredLocally > 0) {
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
@ -1140,7 +1140,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
outp.append((uint32_t)to.adi());
outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
}
@ -1198,7 +1198,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
peer->attemptToContactAt(InetAddress(),a,now,false);
peer->attemptToContactAt(InetAddress(),a,now,false,0);
} else {
TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
}
@ -1217,7 +1217,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
peer->attemptToContactAt(InetAddress(),a,now,false);
peer->attemptToContactAt(InetAddress(),a,now,false,0);
} else {
TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
}
@ -1447,7 +1447,7 @@ void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,cons
outp.append(packetId());
outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
outp.append(nwid);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,_path->nextOutgoingCounter());
_path->send(RR,outp.data(),outp.size(),now);
}
}

View File

@ -180,7 +180,7 @@ public:
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
if (addr.ss_family == AF_INET) {
p->sendHELLO(InetAddress(),addr,_now);
p->sendHELLO(InetAddress(),addr,_now,0);
contacted = true;
break;
}
@ -190,7 +190,7 @@ public:
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
if (addr.ss_family == AF_INET6) {
p->sendHELLO(InetAddress(),addr,_now);
p->sendHELLO(InetAddress(),addr,_now,0);
contacted = true;
break;
}
@ -200,7 +200,7 @@ public:
if ((!contacted)&&(_bestCurrentUpstream)) {
const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
if (up)
p->sendHELLO(up->localAddress(),up->address(),_now);
p->sendHELLO(up->localAddress(),up->address(),_now,up->nextOutgoingCounter());
}
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);

File diff suppressed because it is too large Load Diff

View File

@ -351,7 +351,7 @@ namespace ZeroTier {
* ZeroTier packet
*
* Packet format:
* <[8] 64-bit random packet ID and crypto initialization vector>
* <[8] 64-bit packet ID / crypto IV / packet counter>
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher/hops>
@ -362,6 +362,14 @@ namespace ZeroTier {
*
* Packets smaller than 28 bytes are invalid and silently discarded.
*
* The 64-bit packet ID is a strongly random value used as a crypto IV.
* Its least significant 3 bits are also used as a monotonically increasing
* (and looping) counter for sending packets to a particular recipient. This
* can be used for link quality monitoring and reporting and has no crypto
* impact as it does not increase the likelihood of an IV collision. (The
* crypto we use is not sensitive to the nature of the IV, only that it does
* not repeat.)
*
* The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
* selection allowing up to 7 cipher suites, F is outside-envelope flags,
* and H is hop count.
@ -1102,10 +1110,8 @@ public:
};
#ifdef ZT_TRACE
static const char *verbString(Verb v)
throw();
static const char *errorString(ErrorCode e)
throw();
static const char *verbString(Verb v);
static const char *errorString(ErrorCode e);
#endif
template<unsigned int C2>
@ -1303,6 +1309,12 @@ public:
/**
* Get this packet's unique ID (the IV field interpreted as uint64_t)
*
* Note that the least significant 3 bits of this ID will change when armor()
* is called to armor the packet for transport. This is because armor() will
* mask the last 3 bits against the send counter for QoS monitoring use prior
* to actually using the IV to encrypt and MAC the packet. Be aware of this
* when grabbing the packetId of a new packet prior to armor/send.
*
* @return Packet ID
*/
inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
@ -1337,8 +1349,9 @@ public:
*
* @param key 32-byte key
* @param encryptPayload If true, encrypt packet payload, else just MAC
* @param counter Packet send counter for destination peer -- only least significant 3 bits are used
*/
void armor(const void *key,bool encryptPayload);
void armor(const void *key,bool encryptPayload,unsigned int counter);
/**
* Verify and (if encrypted) decrypt packet

View File

@ -105,6 +105,7 @@ public:
_lastOut(0),
_lastIn(0),
_lastTrustEstablishedPacketReceived(0),
_outgoingPacketCounter(0),
_addr(),
_localAddress(),
_ipScope(InetAddress::IP_SCOPE_NONE)
@ -115,6 +116,7 @@ public:
_lastOut(0),
_lastIn(0),
_lastTrustEstablishedPacketReceived(0),
_outgoingPacketCounter(0),
_addr(addr),
_localAddress(localAddress),
_ipScope(addr.ipScope())
@ -241,10 +243,18 @@ public:
*/
inline uint64_t lastIn() const { return _lastIn; }
/**
* Return and increment outgoing packet counter (used with Packet::armor())
*
* @return Next value that should be used for outgoing packet counter (only least significant 3 bits are used)
*/
inline unsigned int nextOutgoingCounter() { return _outgoingPacketCounter++; }
private:
uint64_t _lastOut;
uint64_t _lastIn;
uint64_t _lastTrustEstablishedPacketReceived;
unsigned int _outgoingPacketCounter;
InetAddress _addr;
InetAddress _localAddress;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often

View File

@ -101,7 +101,7 @@ void Peer::received(
outp.append(redirectTo.rawIpData(),16);
}
outp.append((uint16_t)redirectTo.port());
outp.armor(_key,true);
outp.armor(_key,true,path->nextOutgoingCounter());
path->send(RR,outp.data(),outp.size(),now);
} else {
// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
@ -116,7 +116,7 @@ void Peer::received(
outp.append((uint8_t)16);
outp.append(redirectTo.rawIpData(),16);
}
outp.armor(_key,true);
outp.armor(_key,true,path->nextOutgoingCounter());
path->send(RR,outp.data(),outp.size(),now);
}
suboptimalPath = true;
@ -203,7 +203,7 @@ void Peer::received(
#endif
} else {
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
attemptToContactAt(path->localAddress(),path->address(),now,true);
attemptToContactAt(path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
path->sent(now);
}
}
@ -277,7 +277,7 @@ void Peer::received(
if (count) {
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
outp.armor(_key,true);
outp.armor(_key,true,path->nextOutgoingCounter());
path->send(RR,outp.data(),outp.size(),now);
}
}
@ -342,7 +342,7 @@ SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
}
}
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
{
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
@ -383,22 +383,22 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
RR->node->expectReplyTo(outp.packetId());
if (atAddress) {
outp.armor(_key,false); // false == don't encrypt full payload, but add MAC
outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
} else {
RR->sw->send(outp,false); // false == don't encrypt full payload, but add MAC
}
}
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello)
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
{
if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
RR->node->expectReplyTo(outp.packetId());
outp.armor(_key,true);
outp.armor(_key,true,counter);
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
} else {
sendHELLO(localAddr,atAddress,now);
sendHELLO(localAddr,atAddress,now,counter);
}
}
@ -408,7 +408,7 @@ void Peer::tryMemorizedPath(uint64_t now)
_lastTriedMemorizedPath = now;
InetAddress mp;
if (RR->node->externalPathLookup(_id.address(),-1,mp))
attemptToContactAt(InetAddress(),mp,now,true);
attemptToContactAt(InetAddress(),mp,now,true,0);
}
}
@ -430,7 +430,7 @@ bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
if (bestp >= 0) {
if ( ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) || (_paths[bestp].path->needsHeartbeat(now)) ) {
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false);
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false,_paths[bestp].path->nextOutgoingCounter());
_paths[bestp].path->sent(now);
}
return true;
@ -454,7 +454,7 @@ void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uin
Mutex::Lock _l(_paths_m);
for(unsigned int p=0;p<_numPaths;++p) {
if ( (_paths[p].path->address().ss_family == inetAddressFamily) && (_paths[p].path->address().ipScope() == scope) ) {
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now,false);
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now,false,_paths[p].path->nextOutgoingCounter());
_paths[p].path->sent(now);
_paths[p].lastReceive = 0; // path will not be used unless it speaks again
}

View File

@ -150,8 +150,9 @@ public:
* @param localAddr Local address
* @param atAddress Destination address
* @param now Current time
* @param counter Outgoing packet counter
*/
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now);
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
/**
* Send ECHO (or HELLO for older peers) to this peer at the given address
@ -162,8 +163,9 @@ public:
* @param atAddress Destination address
* @param now Current time
* @param sendFullHello If true, always send a full HELLO instead of just an ECHO
* @param counter Outgoing packet counter
*/
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello);
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
/**
* Try a memorized or statically defined path if any are known

View File

@ -88,7 +88,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
_lastBeaconResponse = now;
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,path->nextOutgoingCounter());
path->send(RR,outp.data(),outp.size(),now);
}
}
@ -777,7 +777,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
if ((clusterMostRecentMemberId < 0)||(viaPath->lastIn() > clusterMostRecentTs)) {
#endif
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now,false);
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
viaPath->sent(now);
}
#ifdef ZT_ENABLE_CLUSTER
@ -825,14 +825,14 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
if (trustedPathId) {
packet.setTrusted(trustedPathId);
} else {
packet.armor((clusterMostRecentMemberId >= 0) ? clusterPeerSecret : peer->key(),encrypt);
packet.armor((clusterMostRecentMemberId >= 0) ? clusterPeerSecret : peer->key(),encrypt,(viaPath) ? viaPath->nextOutgoingCounter() : 0);
}
#else
const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
if (trustedPathId) {
packet.setTrusted(trustedPathId);
} else {
packet.armor(peer->key(),encrypt);
packet.armor(peer->key(),encrypt,viaPath->nextOutgoingCounter());
}
#endif