More multicast work, add a signature in identity for safety margin, cleanup.

This commit is contained in:
Adam Ierymenko 2019-09-09 10:44:31 -07:00
parent 540ee69773
commit 787277d282
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 103 additions and 74 deletions

View file

@ -186,9 +186,9 @@
#define ZT_DEFAULT_MTU 2800 #define ZT_DEFAULT_MTU 2800
/** /**
* Maximum number of packet fragments we'll support (protocol max: 16) * Maximum number of packet fragments we'll support (protocol limit: 16)
*/ */
#define ZT_MAX_PACKET_FRAGMENTS 7 #define ZT_MAX_PACKET_FRAGMENTS 10
/** /**
* Size of RX queue in packets * Size of RX queue in packets

View file

@ -99,18 +99,29 @@ void Identity::generate(const Type t)
} while (_address.isReserved()); } while (_address.isReserved());
delete [] genmem; delete [] genmem;
if (t == P384) if (t == P384) {
ECC384GenerateKey(_pub.p384,_priv.p384); ECC384GenerateKey(_pub.p384,_priv.p384);
SHA384(digest,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
ECC384ECDSASign(_priv.p384,digest,_pub.p384s);
}
} }
bool Identity::locallyValidate() const bool Identity::locallyValidate() const
{ {
uint8_t digest[64];
if (_address.isReserved()) if (_address.isReserved())
return false; return false;
if (_type == P384) {
// Check that the C25519 public key is blessed by the P-384 key.
SHA384(digest,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
if (!ECC384ECDSAVerify(_pub.p384,digest,_pub.p384s))
return false;
}
char *genmem = nullptr; char *genmem = nullptr;
try { try {
uint8_t digest[64];
genmem = new char[ZT_IDENTITY_GEN_MEMORY]; genmem = new char[ZT_IDENTITY_GEN_MEMORY];
_computeMemoryHardHash(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem); _computeMemoryHardHash(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem; delete [] genmem;
@ -151,12 +162,12 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_
*(p++) = ':'; *(p++) = ':';
*(p++) = '1'; *(p++) = '1';
*(p++) = ':'; *(p++) = ':';
int el = Utils::b32e((const uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); int el = Utils::b32e((const uint8_t *)(&_pub),sizeof(_pub),p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf)));
if (el <= 0) return nullptr; if (el <= 0) return nullptr;
p += el; p += el;
if ((_hasPrivate)&&(includePrivate)) { if ((_hasPrivate)&&(includePrivate)) {
*(p++) = ':'; *(p++) = ':';
el = Utils::b32e((const uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); el = Utils::b32e((const uint8_t *)(&_priv),sizeof(_priv),p,(unsigned int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf)));
if (el <= 0) return nullptr; if (el <= 0) return nullptr;
p += el; p += el;
} }
@ -218,7 +229,7 @@ bool Identity::fromString(const char *str)
break; break;
case P384: case P384:
if (Utils::b32d(f,(uint8_t *)(&_pub),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) != (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE)) { if (Utils::b32d(f,(uint8_t *)(&_pub),sizeof(_pub)) != sizeof(_pub)) {
_address.zero(); _address.zero();
return false; return false;
} }
@ -241,7 +252,7 @@ bool Identity::fromString(const char *str)
break; break;
case P384: case P384:
if (Utils::b32d(f,(uint8_t *)(&_priv),ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE) != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)) { if (Utils::b32d(f,(uint8_t *)(&_priv),sizeof(_priv)) != sizeof(_priv)) {
_address.zero(); _address.zero();
return false; return false;
} else { } else {

View file

@ -48,33 +48,24 @@ public:
enum Type enum Type
{ {
C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.x and 2.x, default) C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.x and 2.x, default)
P384 = ZT_CRYPTO_ALG_P384 // Type 1 -- NIST P-384 with linked Curve25519 and Ed25519 secondaries (2.x+) P384 = ZT_CRYPTO_ALG_P384 // Type 1 -- NIST P-384 with linked Curve25519/Ed25519 secondaries (2.x+)
}; };
ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); } ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
ZT_ALWAYS_INLINE Identity(const Identity &id) { memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity)); } ZT_ALWAYS_INLINE Identity(const char *str)
inline Identity(const char *str)
{ {
if (!fromString(str)) if (!fromString(str))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE; throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
} }
template<unsigned int C> template<unsigned int C>
inline Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); } ZT_ALWAYS_INLINE Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); } ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
/** /**
* Set identity to NIL value (all zero) * Set identity to NIL value (all zero)
*/ */
ZT_ALWAYS_INLINE void zero() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); } ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
ZT_ALWAYS_INLINE Identity &operator=(const Identity &id)
{
memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity));
return *this;
}
/** /**
* @return Identity type * @return Identity type
@ -108,7 +99,7 @@ public:
* @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length) * @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length)
* @return True on success, false if no private key * @return True on success, false if no private key
*/ */
inline bool sha512PrivateKey(void *const sha) const ZT_ALWAYS_INLINE bool sha512PrivateKey(void *const sha) const
{ {
if (_hasPrivate) { if (_hasPrivate) {
switch(_type) { switch(_type) {
@ -116,7 +107,7 @@ public:
SHA512(sha,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); SHA512(sha,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
return true; return true;
case P384: case P384:
SHA512(sha,&_priv,ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE); SHA512(sha,&_priv,sizeof(_priv));
return true; return true;
} }
} }
@ -131,7 +122,7 @@ public:
* *
* @param h 128-bit buffer to receive hash (must be 16 bytes in size) * @param h 128-bit buffer to receive hash (must be 16 bytes in size)
*/ */
inline void publicKeyHash128(void *const h) const ZT_ALWAYS_INLINE void publicKeyHash128(void *const h) const
{ {
uint8_t tmp[48]; uint8_t tmp[48];
switch(_type) { switch(_type) {
@ -139,7 +130,7 @@ public:
SHA384(tmp,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); SHA384(tmp,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
break; break;
case P384: case P384:
SHA384(tmp,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE); SHA384(tmp,&_pub,sizeof(_pub));
break; break;
} }
for(int i=0;i<16;++i) for(int i=0;i<16;++i)
@ -158,7 +149,7 @@ public:
* @param siglen Length of buffer * @param siglen Length of buffer
* @return Number of bytes actually written to sig or 0 on error * @return Number of bytes actually written to sig or 0 on error
*/ */
inline unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const ZT_ALWAYS_INLINE unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
{ {
if (_hasPrivate) { if (_hasPrivate) {
switch(_type) { switch(_type) {
@ -171,8 +162,8 @@ public:
case P384: case P384:
if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
// Signature is a hash of the message followed by the c25519/ed25519 type 0 // Signature hash includes the C25519/Ed25519 public key after the message.
// identity public keys to ensure that the two public keys are not separable. // This is an added guard against divorcing these two bound keys.
uint8_t h[48]; uint8_t h[48];
SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
@ -193,7 +184,7 @@ public:
* @param siglen Length of signature in bytes * @param siglen Length of signature in bytes
* @return True if signature validates and data integrity checks * @return True if signature validates and data integrity checks
*/ */
inline bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
{ {
switch(_type) { switch(_type) {
case C25519: case C25519:
@ -218,7 +209,7 @@ public:
* @param key Result parameter to fill with key bytes * @param key Result parameter to fill with key bytes
* @return Was agreement successful? * @return Was agreement successful?
*/ */
inline bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const ZT_ALWAYS_INLINE bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const
{ {
uint8_t rawkey[128]; uint8_t rawkey[128];
uint8_t h[64]; uint8_t h[64];
@ -269,7 +260,7 @@ public:
* @param dest Destination to fill with downgraded identity * @param dest Destination to fill with downgraded identity
* @param toType Desired identity type * @param toType Desired identity type
*/ */
inline bool downgrade(Identity &dest,const Type toType) ZT_ALWAYS_INLINE bool downgrade(Identity &dest,const Type toType)
{ {
if ((_type == P384)&&(toType == C25519)) { if ((_type == P384)&&(toType == C25519)) {
dest._address = _address; dest._address = _address;
@ -289,7 +280,7 @@ public:
* @throws std::out_of_range Buffer too small * @throws std::out_of_range Buffer too small
*/ */
template<unsigned int C> template<unsigned int C>
inline void serialize(Buffer<C> &b,bool includePrivate = false) const ZT_ALWAYS_INLINE void serialize(Buffer<C> &b,bool includePrivate = false) const
{ {
_address.appendTo(b); _address.appendTo(b);
switch(_type) { switch(_type) {
@ -309,6 +300,7 @@ public:
b.append((uint8_t)P384); b.append((uint8_t)P384);
b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
b.append(_pub.p384,ZT_ECC384_PUBLIC_KEY_SIZE); b.append(_pub.p384,ZT_ECC384_PUBLIC_KEY_SIZE);
b.append(_pub.p384s,ZT_ECC384_SIGNATURE_SIZE);
if ((_hasPrivate)&&(includePrivate)) { if ((_hasPrivate)&&(includePrivate)) {
b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)); b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE));
b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
@ -334,7 +326,7 @@ public:
* @throws std::invalid_argument Serialized data invalid * @throws std::invalid_argument Serialized data invalid
*/ */
template<unsigned int C> template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0) ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{ {
_hasPrivate = false; _hasPrivate = false;
unsigned int p = startAt; unsigned int p = startAt;
@ -365,6 +357,8 @@ public:
p += ZT_C25519_PUBLIC_KEY_LEN; p += ZT_C25519_PUBLIC_KEY_LEN;
memcpy(_pub.p384,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE); memcpy(_pub.p384,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
p += ZT_ECC384_PUBLIC_KEY_SIZE; p += ZT_ECC384_PUBLIC_KEY_SIZE;
memcpy(_pub.p384s,b.field(p,ZT_ECC384_SIGNATURE_SIZE),ZT_ECC384_SIGNATURE_SIZE);
p += ZT_ECC384_SIGNATURE_SIZE;
pkl = (unsigned int)b[p++]; pkl = (unsigned int)b[p++];
if (pkl) { if (pkl) {
if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)) if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE))
@ -412,21 +406,21 @@ public:
*/ */
ZT_ALWAYS_INLINE operator bool() const { return (_address); } ZT_ALWAYS_INLINE operator bool() const { return (_address); }
inline bool operator==(const Identity &id) const ZT_ALWAYS_INLINE bool operator==(const Identity &id) const
{ {
if ((_address == id._address)&&(_type == id._type)) { if ((_address == id._address)&&(_type == id._type)) {
switch(_type) { switch(_type) {
case C25519: case C25519:
return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0); return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
case P384: case P384:
return (memcmp(&_pub,&id._pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) == 0); return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
default: default:
return false; return false;
} }
} }
return false; return false;
} }
inline bool operator<(const Identity &id) const ZT_ALWAYS_INLINE bool operator<(const Identity &id) const
{ {
if (_address < id._address) if (_address < id._address)
return true; return true;
@ -438,7 +432,7 @@ public:
case C25519: case C25519:
return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0); return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
case P384: case P384:
return (memcmp(&_pub,&id._pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) < 0); return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
} }
} }
} }
@ -462,6 +456,7 @@ private:
ZT_PACKED_STRUCT(struct { // don't re-order these ZT_PACKED_STRUCT(struct { // don't re-order these
uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN];
uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE];
uint8_t p384s[ZT_ECC384_SIGNATURE_SIZE]; // signature of type 0 key with p384
}) _pub; }) _pub;
}; };

View file

@ -33,35 +33,6 @@ Multicaster::Multicaster(const RuntimeEnvironment *renv) :
Multicaster::~Multicaster() {} Multicaster::~Multicaster() {}
void Multicaster::add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock l(_groups_l);
_groups[Multicaster::Key(nwid,mg)].set(member,now);
}
void Multicaster::addMultiple(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,const unsigned int totalKnown)
{
Mutex::Lock l(_groups_l);
const uint8_t *a = (const uint8_t *)addresses;
Hashtable< Address,int64_t > &members = _groups[Multicaster::Key(nwid,mg)];
while (count--) {
members.set(Address(a,ZT_ADDRESS_LENGTH),now);
a += ZT_ADDRESS_LENGTH;
}
}
void Multicaster::remove(const uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock l(_groups_l);
const Multicaster::Key gk(nwid,mg);
Hashtable< Address,int64_t > *const members = _groups.get(gk);
if (members) {
members->erase(member);
if (members->empty())
_groups.erase(gk);
}
}
void Multicaster::send( void Multicaster::send(
void *tPtr, void *tPtr,
int64_t now, int64_t now,

View file

@ -38,7 +38,7 @@ class Packet;
class Network; class Network;
/** /**
* Multicast database and outbound multicast handler * Multicast database and outbound multicast logic
*/ */
class Multicaster class Multicaster
{ {
@ -54,7 +54,11 @@ public:
* @param mg Multicast group * @param mg Multicast group
* @param member New member address * @param member New member address
*/ */
void add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member); inline void add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock l(_groups_l);
_groups[Multicaster::Key(nwid,mg)].set(member,now);
}
/** /**
* Add multiple addresses from a binary array of 5-byte address fields * Add multiple addresses from a binary array of 5-byte address fields
@ -69,7 +73,16 @@ public:
* @param count Number of addresses * @param count Number of addresses
* @param totalKnown Total number of known addresses as reported by peer * @param totalKnown Total number of known addresses as reported by peer
*/ */
void addMultiple(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,const unsigned int totalKnown); inline void addMultiple(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,const unsigned int totalKnown)
{
Mutex::Lock l(_groups_l);
const uint8_t *a = (const uint8_t *)addresses;
Hashtable< Address,int64_t > &members = _groups[Multicaster::Key(nwid,mg)];
while (count--) {
members.set(Address(a,ZT_ADDRESS_LENGTH),now);
a += ZT_ADDRESS_LENGTH;
}
}
/** /**
* Remove a multicast group member (if present) * Remove a multicast group member (if present)
@ -78,7 +91,17 @@ public:
* @param mg Multicast group * @param mg Multicast group
* @param member Member to unsubscribe * @param member Member to unsubscribe
*/ */
void remove(const uint64_t nwid,const MulticastGroup &mg,const Address &member); inline void remove(const uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock l(_groups_l);
const Multicaster::Key gk(nwid,mg);
Hashtable< Address,int64_t > *const members = _groups.get(gk);
if (members) {
members->erase(member);
if (members->empty())
_groups.erase(gk);
}
}
/** /**
* Iterate over members of a multicast group until function returns false * Iterate over members of a multicast group until function returns false
@ -144,7 +167,7 @@ public:
unsigned int len); unsigned int len);
/** /**
* Clean up and resort database * Clean up database
* *
* @param RR Runtime environment * @param RR Runtime environment
* @param now Current time * @param now Current time

View file

@ -710,17 +710,48 @@ public:
* <[1] flags> * <[1] flags>
* [<[...] network certificate of membership (DEPRECATED)>] * [<[...] network certificate of membership (DEPRECATED)>]
* [<[4] 32-bit implicit gather limit (DEPRECATED)>] * [<[4] 32-bit implicit gather limit (DEPRECATED)>]
* [<[5] ZeroTier address of originating sender (including w/0x08)>]
* [<[2] 16-bit bloom filter multiplier>]
* [<[2] 16-bit length of propagation bloom filter in bytes]
* [<[...] propagation bloom filter>]
* [<[6] source MAC>] * [<[6] source MAC>]
* <[6] destination MAC (multicast address)> * <[6] destination MAC (multicast address)>
* <[4] 32-bit multicast ADI (multicast address extension)> * <[4] 32-bit multicast ADI (multicast address extension)>
* <[2] 16-bit ethertype> * <[2] 16-bit ethertype>
* <[...] ethernet payload> * <[...] ethernet payload>
* [<[2] 16-bit length of signature>]
* [<[...] signature (algorithm depends on sender identity)>]
* *
* Flags: * Flags:
* 0x01 - Network certificate of membership attached (DEPRECATED) * 0x01 - Network certificate of membership attached (DEPRECATED)
* 0x02 - Implicit gather limit field is present (DEPRECATED) * 0x02 - Implicit gather limit field is present (DEPRECATED)
* 0x04 - Source MAC is specified -- otherwise it's computed from sender * 0x04 - Source MAC is specified -- otherwise it's computed from sender
* 0x08 - Explicit recipient list included for P2P/HS replication * 0x08 - Propagation bloom filter is included
* 0x10 - Signature by sending identity is included
*
* Version 1.x only supports sender-side replication. Version 2.x also
* supports peer to peer and hub and spoke models. For that there is
* a new field: a bloom filter that tracks recipients by ZeroTier address.
*
* Bits in the bloom filter are set by multiplying the address by the
* indicated multiplier and then taking that modulo the number of bits
* in the filter. Both the length of the filter and this multiplier are
* variable and can be selected based on the sender's knowledge of
* the total recipient set to minimize the chance of collision, as a
* collision would result in a multicast not reaching one particular
* recipient. The algorithm for selecting these is not defined by the
* protocol.
*
* The ZeroTier address of the originating sender is also included
* before the bloom filter if flag bit 0x08 is set.
*
* Version 2.x also supports an optional signature of the packet's
* payload by the sending ZeroTier node. This can be used to validate
* multicasts propagated cooperatively, since unlike sender side
* replication the message MAC alone cannot be used for this. This
* imposes a non-trivial CPU cost on the sender and so it's optional.
*
* OK is not sent.
* *
* ERROR_MULTICAST_STFU is generated if a recipient no longer wishes to * ERROR_MULTICAST_STFU is generated if a recipient no longer wishes to
* receive these multicasts. It's essentially a source quench. Its * receive these multicasts. It's essentially a source quench. Its
@ -764,8 +795,6 @@ public:
*/ */
VERB_PUSH_DIRECT_PATHS = 0x10, VERB_PUSH_DIRECT_PATHS = 0x10,
// 0x11 -- deprecated
/** /**
* An acknowledgment of receipt of a series of recent packets from another * An acknowledgment of receipt of a series of recent packets from another
* peer. This is used to calculate relative throughput values and to detect * peer. This is used to calculate relative throughput values and to detect