2016-08-04 16:51:15 +00:00
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright ( C ) 2011 - 2016 ZeroTier , Inc . https : //www.zerotier.com/
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2016-09-23 23:08:38 +00:00
# include <algorithm>
2016-08-04 16:51:15 +00:00
# include "Membership.hpp"
# include "RuntimeEnvironment.hpp"
# include "Peer.hpp"
# include "Topology.hpp"
# include "Switch.hpp"
# include "Packet.hpp"
# include "Node.hpp"
2016-09-09 02:48:05 +00:00
# define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 3)
2016-08-04 16:51:15 +00:00
namespace ZeroTier {
2016-09-23 23:08:38 +00:00
Membership : : Membership ( ) :
_lastUpdatedMulticast ( 0 ) ,
_lastPushedCom ( 0 ) ,
2017-04-04 13:47:01 +00:00
_comRevocationThreshold ( 0 ) ,
_revocations ( 4 ) ,
_remoteTags ( 4 ) ,
_remoteCaps ( 4 ) ,
_remoteCoos ( 4 )
2016-09-23 23:08:38 +00:00
{
2017-04-04 15:07:38 +00:00
resetPushState ( ) ;
2016-09-23 23:08:38 +00:00
}
2017-03-28 00:03:17 +00:00
void Membership : : pushCredentials ( const RuntimeEnvironment * RR , void * tPtr , const uint64_t now , const Address & peerAddress , const NetworkConfig & nconf , int localCapabilityIndex , const bool force )
2016-08-04 16:51:15 +00:00
{
2017-02-07 01:10:20 +00:00
bool sendCom = ( ( nconf . com ) & & ( ( ( now - _lastPushedCom ) > = ZT_CREDENTIAL_PUSH_EVERY ) | | ( force ) ) ) ;
2016-08-04 16:51:15 +00:00
2017-02-07 01:10:20 +00:00
const Capability * sendCap ;
if ( localCapabilityIndex > = 0 ) {
sendCap = & ( nconf . capabilities [ localCapabilityIndex ] ) ;
2017-04-04 15:07:38 +00:00
if ( ( ( now - _localCredLastPushed . cap [ localCapabilityIndex ] ) > = ZT_CREDENTIAL_PUSH_EVERY ) | | ( force ) )
_localCredLastPushed . cap [ localCapabilityIndex ] = now ;
else sendCap = ( const Capability * ) 0 ;
2017-02-07 01:10:20 +00:00
} else sendCap = ( const Capability * ) 0 ;
2016-09-23 23:08:38 +00:00
2017-02-07 01:20:22 +00:00
const Tag * sendTags [ ZT_MAX_NETWORK_TAGS ] ;
unsigned int sendTagCount = 0 ;
for ( unsigned int t = 0 ; t < nconf . tagCount ; + + t ) {
2017-04-04 15:07:38 +00:00
if ( ( ( now - _localCredLastPushed . tag [ t ] ) > = ZT_CREDENTIAL_PUSH_EVERY ) | | ( force ) ) {
_localCredLastPushed . tag [ t ] = now ;
2017-02-07 01:20:22 +00:00
sendTags [ sendTagCount + + ] = & ( nconf . tags [ t ] ) ;
}
}
2017-02-23 19:47:36 +00:00
const CertificateOfOwnership * sendCoos [ ZT_MAX_CERTIFICATES_OF_OWNERSHIP ] ;
unsigned int sendCooCount = 0 ;
for ( unsigned int c = 0 ; c < nconf . certificateOfOwnershipCount ; + + c ) {
2017-04-04 15:07:38 +00:00
if ( ( ( now - _localCredLastPushed . coo [ c ] ) > = ZT_CREDENTIAL_PUSH_EVERY ) | | ( force ) ) {
_localCredLastPushed . coo [ c ] = now ;
2017-02-23 19:47:36 +00:00
sendCoos [ sendCooCount + + ] = & ( nconf . certificatesOfOwnership [ c ] ) ;
}
}
2017-02-07 01:10:20 +00:00
unsigned int tagPtr = 0 ;
2017-02-23 19:47:36 +00:00
unsigned int cooPtr = 0 ;
while ( ( tagPtr < sendTagCount ) | | ( cooPtr < sendCooCount ) | | ( sendCom ) | | ( sendCap ) ) {
2017-02-07 01:10:20 +00:00
Packet outp ( peerAddress , RR - > identity . address ( ) , Packet : : VERB_NETWORK_CREDENTIALS ) ;
2016-09-23 23:08:38 +00:00
2017-02-07 01:10:20 +00:00
if ( sendCom ) {
sendCom = false ;
nconf . com . serialize ( outp ) ;
_lastPushedCom = now ;
}
outp . append ( ( uint8_t ) 0x00 ) ;
2016-08-04 16:51:15 +00:00
2017-02-07 01:10:20 +00:00
if ( sendCap ) {
outp . append ( ( uint16_t ) 1 ) ;
sendCap - > serialize ( outp ) ;
sendCap = ( const Capability * ) 0 ;
} else outp . append ( ( uint16_t ) 0 ) ;
2016-08-04 16:51:15 +00:00
2017-02-07 01:10:20 +00:00
const unsigned int tagCountAt = outp . size ( ) ;
outp . addSize ( 2 ) ;
unsigned int thisPacketTagCount = 0 ;
2017-02-23 19:47:36 +00:00
while ( ( tagPtr < sendTagCount ) & & ( ( outp . size ( ) + sizeof ( Tag ) + 16 ) < ZT_PROTO_MAX_PACKET_LENGTH ) ) {
2017-02-07 22:06:40 +00:00
sendTags [ tagPtr + + ] - > serialize ( outp ) ;
+ + thisPacketTagCount ;
2017-02-07 01:10:20 +00:00
}
outp . setAt ( tagCountAt , ( uint16_t ) thisPacketTagCount ) ;
// No revocations, these propagate differently
outp . append ( ( uint16_t ) 0 ) ;
2017-02-23 19:47:36 +00:00
const unsigned int cooCountAt = outp . size ( ) ;
outp . addSize ( 2 ) ;
unsigned int thisPacketCooCount = 0 ;
while ( ( cooPtr < sendCooCount ) & & ( ( outp . size ( ) + sizeof ( CertificateOfOwnership ) + 16 ) < ZT_PROTO_MAX_PACKET_LENGTH ) ) {
sendCoos [ cooPtr + + ] - > serialize ( outp ) ;
+ + thisPacketCooCount ;
}
outp . setAt ( cooCountAt , ( uint16_t ) thisPacketCooCount ) ;
2017-02-07 01:10:20 +00:00
outp . compress ( ) ;
2017-03-28 00:03:17 +00:00
RR - > sw - > send ( tPtr , outp , true ) ;
2016-08-04 16:51:15 +00:00
}
}
2017-03-28 00:03:17 +00:00
Membership : : AddCredentialResult Membership : : addCredential ( const RuntimeEnvironment * RR , void * tPtr , const NetworkConfig & nconf , const CertificateOfMembership & com )
2016-09-23 23:08:38 +00:00
{
2017-04-04 13:47:01 +00:00
const uint64_t newts = com . timestamp ( ) ;
2016-09-23 23:08:38 +00:00
if ( newts < = _comRevocationThreshold ) {
TRACE ( " addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (revoked) " , com . issuedTo ( ) . toString ( ) . c_str ( ) , com . networkId ( ) ) ;
return ADD_REJECTED ;
2016-08-24 22:45:37 +00:00
}
2016-09-07 22:15:52 +00:00
2017-04-04 13:47:01 +00:00
const uint64_t oldts = _com . timestamp ( ) ;
2016-09-23 23:08:38 +00:00
if ( newts < oldts ) {
TRACE ( " addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (older than current) " , com . issuedTo ( ) . toString ( ) . c_str ( ) , com . networkId ( ) ) ;
return ADD_REJECTED ;
}
if ( ( newts = = oldts ) & & ( _com = = com ) ) {
TRACE ( " addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (redundant) " , com . issuedTo ( ) . toString ( ) . c_str ( ) , com . networkId ( ) ) ;
return ADD_ACCEPTED_REDUNDANT ;
}
2016-09-07 22:15:52 +00:00
2017-03-28 00:03:17 +00:00
switch ( com . verify ( RR , tPtr ) ) {
2016-09-23 23:08:38 +00:00
default :
TRACE ( " addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (invalid signature or object) " , com . issuedTo ( ) . toString ( ) . c_str ( ) , com . networkId ( ) ) ;
return ADD_REJECTED ;
case 0 :
2016-09-09 02:48:05 +00:00
TRACE ( " addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (new) " , com . issuedTo ( ) . toString ( ) . c_str ( ) , com . networkId ( ) ) ;
2016-08-24 22:45:37 +00:00
_com = com ;
2016-09-23 23:08:38 +00:00
return ADD_ACCEPTED_NEW ;
case 1 :
return ADD_DEFERRED_FOR_WHOIS ;
2016-08-24 22:45:37 +00:00
}
2016-08-04 16:51:15 +00:00
}
2017-04-04 15:07:38 +00:00
// Template out addCredential() for many cred types to avoid copypasta
2017-04-04 13:47:01 +00:00
template < typename C >
static Membership : : AddCredentialResult _addCredImpl ( Hashtable < uint32_t , C > & remoteCreds , const Hashtable < uint64_t , uint64_t > & revocations , const RuntimeEnvironment * RR , void * tPtr , const NetworkConfig & nconf , const C & cred )
2016-08-04 16:51:15 +00:00
{
2017-04-04 13:47:01 +00:00
C * rc = remoteCreds . get ( cred . id ( ) ) ;
if ( rc ) {
2017-04-17 16:30:28 +00:00
if ( rc - > timestamp ( ) > cred . timestamp ( ) ) {
2017-04-04 13:47:01 +00:00
TRACE ( " addCredential(type==%d) for %s on %.16llx REJECTED (older than credential we have) " , ( int ) C : : credentialType ( ) , cred . issuedTo ( ) . toString ( ) . c_str ( ) , cred . networkId ( ) ) ;
return Membership : : ADD_REJECTED ;
2016-09-23 23:08:38 +00:00
}
2017-04-04 13:47:01 +00:00
if ( * rc = = cred ) {
2017-04-17 16:31:07 +00:00
//TRACE("addCredential(type==%d) for %s on %.16llx ACCEPTED (redundant)",(int)C::credentialType(),cred.issuedTo().toString().c_str(),cred.networkId());
2017-04-04 13:47:01 +00:00
return Membership : : ADD_ACCEPTED_REDUNDANT ;
2016-09-23 23:08:38 +00:00
}
2016-08-24 22:45:37 +00:00
}
2016-09-23 23:08:38 +00:00
2017-04-04 15:07:38 +00:00
const uint64_t * const rt = revocations . get ( Membership : : credentialKey ( C : : credentialType ( ) , cred . id ( ) ) ) ;
2017-04-04 13:47:01 +00:00
if ( ( rt ) & & ( * rt > = cred . timestamp ( ) ) ) {
TRACE ( " addCredential(type==%d) for %s on %.16llx REJECTED (timestamp below revocation threshold) " , ( int ) C : : credentialType ( ) , cred . issuedTo ( ) . toString ( ) . c_str ( ) , cred . networkId ( ) ) ;
return Membership : : ADD_REJECTED ;
}
switch ( cred . verify ( RR , tPtr ) ) {
2016-09-23 23:08:38 +00:00
default :
2017-04-04 13:47:01 +00:00
TRACE ( " addCredential(type==%d) for %s on %.16llx REJECTED (invalid) " , ( int ) C : : credentialType ( ) , cred . issuedTo ( ) . toString ( ) . c_str ( ) , cred . networkId ( ) ) ;
return Membership : : ADD_REJECTED ;
2016-09-23 23:08:38 +00:00
case 0 :
2017-04-04 13:47:01 +00:00
TRACE ( " addCredential(type==%d) for %s on %.16llx ACCEPTED (new) " , ( int ) C : : credentialType ( ) , cred . issuedTo ( ) . toString ( ) . c_str ( ) , cred . networkId ( ) ) ;
if ( ! rc )
rc = & ( remoteCreds [ cred . id ( ) ] ) ;
* rc = cred ;
return Membership : : ADD_ACCEPTED_NEW ;
2016-09-23 23:08:38 +00:00
case 1 :
2017-04-04 13:47:01 +00:00
return Membership : : ADD_DEFERRED_FOR_WHOIS ;
2016-08-04 16:51:15 +00:00
}
}
2017-04-04 15:07:38 +00:00
Membership : : AddCredentialResult Membership : : addCredential ( const RuntimeEnvironment * RR , void * tPtr , const NetworkConfig & nconf , const Tag & tag ) { return _addCredImpl < Tag > ( _remoteTags , _revocations , RR , tPtr , nconf , tag ) ; }
Membership : : AddCredentialResult Membership : : addCredential ( const RuntimeEnvironment * RR , void * tPtr , const NetworkConfig & nconf , const Capability & cap ) { return _addCredImpl < Capability > ( _remoteCaps , _revocations , RR , tPtr , nconf , cap ) ; }
Membership : : AddCredentialResult Membership : : addCredential ( const RuntimeEnvironment * RR , void * tPtr , const NetworkConfig & nconf , const CertificateOfOwnership & coo ) { return _addCredImpl < CertificateOfOwnership > ( _remoteCoos , _revocations , RR , tPtr , nconf , coo ) ; }
2016-08-04 16:51:15 +00:00
2017-03-28 00:03:17 +00:00
Membership : : AddCredentialResult Membership : : addCredential ( const RuntimeEnvironment * RR , void * tPtr , const NetworkConfig & nconf , const Revocation & rev )
2016-09-26 23:17:02 +00:00
{
2017-04-04 13:47:01 +00:00
uint64_t * rt ;
2017-03-28 00:03:17 +00:00
switch ( rev . verify ( RR , tPtr ) ) {
2016-09-26 23:17:02 +00:00
default :
return ADD_REJECTED ;
case 0 : {
2017-04-04 13:47:01 +00:00
const Credential : : Type ct = rev . type ( ) ;
switch ( ct ) {
case Credential : : CREDENTIAL_TYPE_COM :
if ( rev . threshold ( ) > _comRevocationThreshold ) {
_comRevocationThreshold = rev . threshold ( ) ;
return ADD_ACCEPTED_NEW ;
}
return ADD_ACCEPTED_REDUNDANT ;
case Credential : : CREDENTIAL_TYPE_CAPABILITY :
case Credential : : CREDENTIAL_TYPE_TAG :
case Credential : : CREDENTIAL_TYPE_COO :
2017-04-04 15:07:38 +00:00
rt = & ( _revocations [ credentialKey ( ct , rev . credentialId ( ) ) ] ) ;
2017-04-04 13:47:01 +00:00
if ( * rt < rev . threshold ( ) ) {
* rt = rev . threshold ( ) ;
return ADD_ACCEPTED_NEW ;
}
return ADD_ACCEPTED_REDUNDANT ;
2016-09-26 23:17:02 +00:00
default :
2017-03-13 13:53:23 +00:00
return ADD_REJECTED ;
2016-09-26 23:17:02 +00:00
}
}
case 1 :
return ADD_DEFERRED_FOR_WHOIS ;
}
}
2017-04-04 15:07:38 +00:00
void Membership : : clean ( const uint64_t now , const NetworkConfig & nconf )
{
_cleanCredImpl < Tag > ( nconf , _remoteTags ) ;
_cleanCredImpl < Capability > ( nconf , _remoteCaps ) ;
_cleanCredImpl < CertificateOfOwnership > ( nconf , _remoteCoos ) ;
}
2016-08-04 16:51:15 +00:00
} // namespace ZeroTier