2013-07-04 20:56:19 +00:00
/*
2015-02-17 21:11:34 +00:00
* ZeroTier One - Network Virtualization Everywhere
* Copyright ( C ) 2011 - 2015 ZeroTier , Inc .
2013-07-04 20:56:19 +00:00
*
* 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/>.
*
* - -
*
* ZeroTier may be used and distributed under the terms of the GPLv3 , which
* are available at : http : //www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form , please contact ZeroTier Networks
* LLC . Start here : http : //www.zerotier.com/
*/
2013-08-06 04:05:39 +00:00
# include <stdio.h>
# include <string.h>
2013-07-29 17:56:20 +00:00
# include <stdlib.h>
# include <math.h>
2014-04-07 22:39:33 +00:00
# include "Constants.hpp"
2013-10-18 16:01:48 +00:00
# include "Network.hpp"
2013-07-29 17:56:20 +00:00
# include "RuntimeEnvironment.hpp"
2013-07-04 20:56:19 +00:00
# include "Switch.hpp"
2013-07-30 15:14:53 +00:00
# include "Packet.hpp"
2013-10-16 21:47:26 +00:00
# include "Buffer.hpp"
2015-04-02 02:09:18 +00:00
# include "NetworkConfigMaster.hpp"
2013-07-04 20:56:19 +00:00
namespace ZeroTier {
2014-05-23 22:13:34 +00:00
const ZeroTier : : MulticastGroup Network : : BROADCAST ( ZeroTier : : MAC ( 0xff ) , 0 ) ;
2015-04-02 02:09:18 +00:00
Network : : Network ( const RuntimeEnvironment * renv , uint64_t nwid ) :
RR ( renv ) ,
_id ( nwid ) ,
_mac ( renv - > identity . address ( ) , nwid ) ,
_enabled ( true ) ,
_lastConfigUpdate ( 0 ) ,
_destroyed ( false ) ,
_netconfFailure ( NETCONF_FAILURE_NONE )
2013-08-08 14:41:17 +00:00
{
2015-04-02 02:09:18 +00:00
char confn [ 128 ] , mcdbn [ 128 ] ;
Utils : : snprintf ( confn , sizeof ( confn ) , " networks.d/%.16llx.conf " , _id ) ;
Utils : : snprintf ( mcdbn , sizeof ( mcdbn ) , " networks.d/%.16llx.mcerts " , _id ) ;
if ( _id = = ZT_TEST_NETWORK_ID ) {
applyConfiguration ( NetworkConfig : : createTestNetworkConfig ( RR - > identity . address ( ) ) ) ;
// Save a one-byte CR to persist membership in the test network
RR - > node - > dataStorePut ( confn , " \n " , 1 , false ) ;
} else {
bool gotConf = false ;
try {
std : : string conf ( RR - > node - > dataStoreGet ( confn ) ) ;
if ( conf . length ( ) ) {
setConfiguration ( Dictionary ( conf ) , false ) ;
gotConf = true ;
}
} catch ( . . . ) { } // ignore invalids, we'll re-request
if ( ! gotConf ) {
// Save a one-byte CR to persist membership while we request a real netconf
RR - > node - > dataStorePut ( confn , " \n " , 1 , false ) ;
}
try {
std : : string mcdb ( RR - > node - > dataStoreGet ( mcdbn ) ) ;
if ( mcdb . length ( ) > 6 ) {
const char * p = mcdb . data ( ) ;
const char * e = p + mcdb . length ( ) ;
if ( ! memcmp ( " ZTMCD0 " , p , 6 ) ) {
p + = 6 ;
Mutex : : Lock _l ( _lock ) ;
while ( p ! = e ) {
CertificateOfMembership com ;
com . deserialize2 ( p , e ) ;
if ( ! com )
break ;
_membershipCertificates . insert ( std : : pair < Address , CertificateOfMembership > ( com . issuedTo ( ) , com ) ) ;
}
}
}
} catch ( . . . ) { } // ignore invalid MCDB, we'll re-learn from peers
2013-08-08 14:41:17 +00:00
}
2015-04-02 02:09:18 +00:00
requestConfiguration ( ) ;
2013-08-08 14:41:17 +00:00
}
2013-08-06 05:28:56 +00:00
Network : : ~ Network ( )
{
2014-08-22 00:49:05 +00:00
if ( _destroyed ) {
2015-04-02 02:09:18 +00:00
char n [ 128 ] ;
Utils : : snprintf ( n , sizeof ( n ) , " networks.d/%.16llx.conf " , _id ) ;
RR - > node - > dataStoreDelete ( n ) ;
Utils : : snprintf ( n , sizeof ( n ) , " networks.d/%.16llx.mcerts " , _id ) ;
RR - > node - > dataStoreDelete ( n ) ;
2013-08-06 05:28:56 +00:00
} else {
clean ( ) ;
2014-09-26 19:23:43 +00:00
_dumpMembershipCerts ( ) ;
2013-08-06 05:28:56 +00:00
}
}
2014-10-04 01:27:42 +00:00
// Function object used by rescanMulticastGroups()
class AnnounceMulticastGroupsToPeersWithActiveDirectPaths
2014-06-13 21:06:34 +00:00
{
2014-10-04 01:27:42 +00:00
public :
AnnounceMulticastGroupsToPeersWithActiveDirectPaths ( const RuntimeEnvironment * renv , Network * nw ) :
RR ( renv ) ,
_now ( Utils : : now ( ) ) ,
2014-10-14 23:38:27 +00:00
_network ( nw ) ,
_supernodeAddresses ( renv - > topology - > supernodeAddresses ( ) )
2014-10-04 01:27:42 +00:00
{ }
inline void operator ( ) ( Topology & t , const SharedPtr < Peer > & p )
{
2014-10-14 23:38:27 +00:00
if ( ( ( p - > hasActiveDirectPath ( _now ) ) & & ( _network - > isAllowed ( p - > address ( ) ) ) ) | | ( std : : find ( _supernodeAddresses . begin ( ) , _supernodeAddresses . end ( ) , p - > address ( ) ) ! = _supernodeAddresses . end ( ) ) ) {
2014-10-04 01:27:42 +00:00
Packet outp ( p - > address ( ) , RR - > identity . address ( ) , Packet : : VERB_MULTICAST_LIKE ) ;
std : : set < MulticastGroup > mgs ( _network - > multicastGroups ( ) ) ;
for ( std : : set < MulticastGroup > : : iterator mg ( mgs . begin ( ) ) ; mg ! = mgs . end ( ) ; + + mg ) {
if ( ( outp . size ( ) + 18 ) > ZT_UDP_DEFAULT_PAYLOAD_MTU ) {
outp . armor ( p - > key ( ) , true ) ;
p - > send ( RR , outp . data ( ) , outp . size ( ) , _now ) ;
outp . reset ( p - > address ( ) , RR - > identity . address ( ) , Packet : : VERB_MULTICAST_LIKE ) ;
}
// network ID, MAC, ADI
outp . append ( ( uint64_t ) _network - > id ( ) ) ;
mg - > mac ( ) . appendTo ( outp ) ;
outp . append ( ( uint32_t ) mg - > adi ( ) ) ;
}
if ( outp . size ( ) > ZT_PROTO_MIN_PACKET_LENGTH ) {
outp . armor ( p - > key ( ) , true ) ;
p - > send ( RR , outp . data ( ) , outp . size ( ) , _now ) ;
2014-06-13 21:06:34 +00:00
}
}
2014-10-04 01:27:42 +00:00
}
private :
const RuntimeEnvironment * RR ;
uint64_t _now ;
Network * _network ;
2014-10-14 23:38:27 +00:00
std : : vector < Address > _supernodeAddresses ;
2014-10-04 01:27:42 +00:00
} ;
2014-10-03 23:14:34 +00:00
bool Network : : applyConfiguration ( const SharedPtr < NetworkConfig > & conf )
2013-07-29 17:56:20 +00:00
{
2014-01-28 07:13:36 +00:00
Mutex : : Lock _l ( _lock ) ;
2014-08-22 00:49:05 +00:00
if ( _destroyed )
return false ;
2013-09-04 13:27:56 +00:00
try {
2014-10-03 23:14:34 +00:00
if ( ( conf - > networkId ( ) = = _id ) & & ( conf - > issuedTo ( ) = = RR - > identity . address ( ) ) ) {
_config = conf ;
2015-04-02 02:09:18 +00:00
_lastConfigUpdate = RR - > node - > now ( ) ;
2014-01-28 07:13:36 +00:00
_netconfFailure = NETCONF_FAILURE_NONE ;
return true ;
2013-10-18 16:01:48 +00:00
} else {
LOG ( " ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address) " , ( unsigned long long ) _id ) ;
2013-08-06 04:05:39 +00:00
}
2013-10-18 16:01:48 +00:00
} catch ( std : : exception & exc ) {
LOG ( " ignored invalid configuration for network %.16llx (%s) " , ( unsigned long long ) _id , exc . what ( ) ) ;
2013-09-04 13:27:56 +00:00
} catch ( . . . ) {
2013-10-18 16:01:48 +00:00
LOG ( " ignored invalid configuration for network %.16llx (unknown exception) " , ( unsigned long long ) _id ) ;
2013-07-30 15:14:53 +00:00
}
2014-01-28 07:13:36 +00:00
return false ;
2013-07-29 17:56:20 +00:00
}
2015-01-06 01:47:59 +00:00
int Network : : setConfiguration ( const Dictionary & conf , bool saveToDisk )
2014-10-03 23:14:34 +00:00
{
try {
SharedPtr < NetworkConfig > newConfig ( new NetworkConfig ( conf ) ) ; // throws if invalid
2015-01-06 01:47:59 +00:00
{
Mutex : : Lock _l ( _lock ) ;
if ( ( _config ) & & ( * _config = = * newConfig ) )
2015-01-09 21:35:20 +00:00
return 1 ; // OK config, but duplicate of what we already have
2015-01-06 01:47:59 +00:00
}
2014-10-03 23:14:34 +00:00
if ( applyConfiguration ( newConfig ) ) {
if ( saveToDisk ) {
2015-04-02 02:09:18 +00:00
char n [ 128 ] ;
Utils : : snprintf ( n , sizeof ( n ) , " networks.d/%.16llx.conf " , _id ) ;
RR - > node - > dataStorePut ( n , conf . toString ( ) , true ) ;
2014-10-03 23:14:34 +00:00
}
2015-01-06 01:47:59 +00:00
return 2 ; // OK and configuration has changed
2014-10-03 23:14:34 +00:00
}
} catch ( . . . ) {
LOG ( " ignored invalid configuration for network %.16llx (dictionary decode failed) " , ( unsigned long long ) _id ) ;
}
2015-01-06 01:47:59 +00:00
return 0 ;
2014-10-03 23:14:34 +00:00
}
2013-07-29 17:56:20 +00:00
void Network : : requestConfiguration ( )
{
2014-10-03 23:14:34 +00:00
if ( _id = = ZT_TEST_NETWORK_ID ) // pseudo-network-ID, no netconf master
return ;
2014-09-24 20:53:03 +00:00
if ( controller ( ) = = RR - > identity . address ( ) ) {
2015-04-02 02:09:18 +00:00
if ( RR - > netconfMaster ) {
SharedPtr < NetworkConfig > nconf ( config2 ( ) ) ;
Dictionary newconf ;
switch ( RR - > netconfMaster - > doNetworkConfigRequest ( InetAddress ( ) , RR - > identity , _id , Dictionary ( ) , ( nconf ) ? nconf - > revision ( ) : ( uint64_t ) 0 , newconf ) ) {
case NetworkConfigMaster : : NETCONF_QUERY_OK :
this - > setConfiguration ( newconf , true ) ;
return ;
case NetworkConfigMaster : : NETCONF_QUERY_OBJECT_NOT_FOUND :
this - > setNotFound ( ) ;
return ;
case NetworkConfigMaster : : NETCONF_QUERY_ACCESS_DENIED :
this - > setAccessDenied ( ) ;
return ;
default :
return ;
}
} else {
this - > setNotFound ( ) ;
return ;
}
2013-08-05 20:06:16 +00:00
}
2013-09-24 21:35:05 +00:00
2013-08-05 20:06:16 +00:00
TRACE ( " requesting netconf for network %.16llx from netconf master %s " , ( unsigned long long ) _id , controller ( ) . toString ( ) . c_str ( ) ) ;
2014-09-24 20:53:03 +00:00
Packet outp ( controller ( ) , RR - > identity . address ( ) , Packet : : VERB_NETWORK_CONFIG_REQUEST ) ;
2013-07-30 15:14:53 +00:00
outp . append ( ( uint64_t ) _id ) ;
2013-08-06 04:05:39 +00:00
outp . append ( ( uint16_t ) 0 ) ; // no meta-data
2015-01-06 01:51:50 +00:00
{
Mutex : : Lock _l ( _lock ) ;
if ( _config )
2015-04-02 02:09:18 +00:00
outp . append ( ( uint64_t ) _config - > revision ( ) ) ;
2015-01-06 01:51:50 +00:00
else outp . append ( ( uint64_t ) 0 ) ;
}
2014-09-24 20:53:03 +00:00
RR - > sw - > send ( outp , true ) ;
2013-07-29 17:56:20 +00:00
}
2014-10-01 00:26:34 +00:00
void Network : : addMembershipCertificate ( const CertificateOfMembership & cert , bool forceAccept )
2013-08-06 04:05:39 +00:00
{
2013-10-25 17:43:04 +00:00
if ( ! cert ) // sanity check
return ;
2014-11-13 20:40:51 +00:00
Mutex : : Lock _l ( _lock ) ;
CertificateOfMembership & old = _membershipCertificates [ cert . issuedTo ( ) ] ;
// Nothing to do if the cert hasn't changed -- we get duplicates due to zealous cert pushing
if ( old = = cert )
return ;
// Check signature, log and return if cert is invalid
2014-10-01 00:26:34 +00:00
if ( ! forceAccept ) {
2014-10-14 19:37:35 +00:00
if ( cert . signedBy ( ) ! = controller ( ) ) {
LOG ( " rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network " , ( unsigned long long ) _id , cert . signedBy ( ) . toString ( ) . c_str ( ) ) ;
2014-10-01 00:26:34 +00:00
return ;
2014-10-14 19:37:35 +00:00
}
2014-10-01 00:26:34 +00:00
SharedPtr < Peer > signer ( RR - > topology - > getPeer ( cert . signedBy ( ) ) ) ;
2014-10-14 19:37:35 +00:00
if ( ! signer ) {
// This would be rather odd, since this is our netconf master... could happen
// if we get packets before we've gotten config.
RR - > sw - > requestWhois ( cert . signedBy ( ) ) ;
2014-10-01 00:26:34 +00:00
return ;
2014-10-14 19:37:35 +00:00
}
if ( ! cert . verify ( signer - > identity ( ) ) ) {
LOG ( " rejected network membership certificate for %.16llx signed by %s: signature check failed " , ( unsigned long long ) _id , cert . signedBy ( ) . toString ( ) . c_str ( ) ) ;
return ;
}
2014-10-01 00:26:34 +00:00
}
2014-11-13 20:40:51 +00:00
// If we made it past authentication, update cert
2015-01-07 01:16:54 +00:00
if ( cert . revision ( ) ! = old . revision ( ) )
2013-10-17 10:41:52 +00:00
old = cert ;
2013-08-06 04:05:39 +00:00
}
2014-10-09 19:42:25 +00:00
bool Network : : peerNeedsOurMembershipCertificate ( const Address & to , uint64_t now )
{
Mutex : : Lock _l ( _lock ) ;
if ( ( _config ) & & ( ! _config - > isPublic ( ) ) & & ( _config - > com ( ) ) ) {
2015-01-07 01:16:54 +00:00
uint64_t & lastPushed = _lastPushedMembershipCertificate [ to ] ;
if ( ( now - lastPushed ) > ( ZT_NETWORK_AUTOCONF_DELAY / 2 ) ) {
lastPushed = now ;
return true ;
2014-10-09 19:42:25 +00:00
}
}
return false ;
}
2013-07-29 21:11:00 +00:00
bool Network : : isAllowed ( const Address & peer ) const
{
try {
Mutex : : Lock _l ( _lock ) ;
2013-10-18 16:01:48 +00:00
if ( ! _config )
return false ;
2014-06-14 20:24:19 +00:00
if ( _config - > isPublic ( ) )
2013-10-18 16:01:48 +00:00
return true ;
2013-09-11 19:13:05 +00:00
std : : map < Address , CertificateOfMembership > : : const_iterator pc ( _membershipCertificates . find ( peer ) ) ;
2013-07-29 21:11:00 +00:00
if ( pc = = _membershipCertificates . end ( ) )
2013-10-16 21:47:26 +00:00
return false ; // no certificate on file
2014-10-09 19:42:25 +00:00
2013-10-18 16:01:48 +00:00
return _config - > com ( ) . agreesWith ( pc - > second ) ; // is other cert valid against ours?
2013-07-29 21:11:00 +00:00
} catch ( std : : exception & exc ) {
TRACE ( " isAllowed() check failed for peer %s: unexpected exception: %s " , peer . toString ( ) . c_str ( ) , exc . what ( ) ) ;
} catch ( . . . ) {
TRACE ( " isAllowed() check failed for peer %s: unexpected exception: unknown exception " , peer . toString ( ) . c_str ( ) ) ;
}
2013-10-16 21:47:26 +00:00
return false ; // default position on any failure
2013-07-29 21:11:00 +00:00
}
void Network : : clean ( )
{
2014-09-24 16:01:58 +00:00
uint64_t now = Utils : : now ( ) ;
2014-09-30 23:28:25 +00:00
Mutex : : Lock _l ( _lock ) ;
2014-08-22 00:49:05 +00:00
2014-09-30 23:28:25 +00:00
if ( _destroyed )
return ;
2014-08-22 00:49:05 +00:00
2014-09-30 23:28:25 +00:00
if ( ( _config ) & & ( _config - > isPublic ( ) ) ) {
// Open (public) networks do not track certs or cert pushes at all.
_membershipCertificates . clear ( ) ;
_lastPushedMembershipCertificate . clear ( ) ;
} else if ( _config ) {
// Clean certificates that are no longer valid from the cache.
for ( std : : map < Address , CertificateOfMembership > : : iterator c = ( _membershipCertificates . begin ( ) ) ; c ! = _membershipCertificates . end ( ) ; ) {
if ( _config - > com ( ) . agreesWith ( c - > second ) )
+ + c ;
else _membershipCertificates . erase ( c + + ) ;
2013-08-06 04:05:39 +00:00
}
2014-09-30 23:28:25 +00:00
// Clean entries from the last pushed tracking map if they're so old as
// to be no longer relevant.
2015-01-07 01:16:54 +00:00
uint64_t forgetIfBefore = now - ( ZT_PEER_ACTIVITY_TIMEOUT * 16 ) ; // arbitrary reasonable cutoff
2014-09-30 23:28:25 +00:00
for ( std : : map < Address , uint64_t > : : iterator lp ( _lastPushedMembershipCertificate . begin ( ) ) ; lp ! = _lastPushedMembershipCertificate . end ( ) ; ) {
if ( lp - > second < forgetIfBefore )
_lastPushedMembershipCertificate . erase ( lp + + ) ;
else + + lp ;
2013-08-03 16:53:46 +00:00
}
2013-07-29 21:11:00 +00:00
}
2014-09-30 23:28:25 +00:00
// Clean learned multicast groups if we haven't heard from them in a while
for ( std : : map < MulticastGroup , uint64_t > : : iterator mg ( _multicastGroupsBehindMe . begin ( ) ) ; mg ! = _multicastGroupsBehindMe . end ( ) ; ) {
if ( ( now - mg - > second ) > ( ZT_MULTICAST_LIKE_EXPIRE * 2 ) )
_multicastGroupsBehindMe . erase ( mg + + ) ;
else + + mg ;
2014-06-13 21:06:34 +00:00
}
}
Network : : Status Network : : status ( ) const
{
Mutex : : Lock _l ( _lock ) ;
2014-08-22 00:49:05 +00:00
switch ( _netconfFailure ) {
case NETCONF_FAILURE_ACCESS_DENIED :
return NETWORK_ACCESS_DENIED ;
case NETCONF_FAILURE_NOT_FOUND :
return NETWORK_NOT_FOUND ;
case NETCONF_FAILURE_NONE :
return ( ( _lastConfigUpdate > 0 ) ? ( ( _tap ) ? NETWORK_OK : NETWORK_INITIALIZING ) : NETWORK_WAITING_FOR_FIRST_AUTOCONF ) ;
//case NETCONF_FAILURE_INIT_FAILED:
default :
return NETWORK_INITIALIZATION_FAILED ;
}
2013-07-29 21:11:00 +00:00
}
2014-09-26 19:23:43 +00:00
void Network : : learnBridgeRoute ( const MAC & mac , const Address & addr )
2013-07-04 20:56:19 +00:00
{
2014-09-26 19:23:43 +00:00
Mutex : : Lock _l ( _lock ) ;
_remoteBridgeRoutes [ mac ] = addr ;
2013-10-16 21:47:26 +00:00
2014-09-26 19:23:43 +00:00
// If _remoteBridgeRoutes exceeds sanity limit, trim worst offenders until below -- denial of service circuit breaker
while ( _remoteBridgeRoutes . size ( ) > ZT_MAX_BRIDGE_ROUTES ) {
std : : map < Address , unsigned long > counts ;
Address maxAddr ;
unsigned long maxCount = 0 ;
for ( std : : map < MAC , Address > : : iterator br ( _remoteBridgeRoutes . begin ( ) ) ; br ! = _remoteBridgeRoutes . end ( ) ; + + br ) {
unsigned long c = + + counts [ br - > second ] ;
if ( c > maxCount ) {
maxCount = c ;
maxAddr = br - > second ;
}
}
for ( std : : map < MAC , Address > : : iterator br ( _remoteBridgeRoutes . begin ( ) ) ; br ! = _remoteBridgeRoutes . end ( ) ; ) {
if ( br - > second = = maxAddr )
_remoteBridgeRoutes . erase ( br + + ) ;
else + + br ;
}
2013-07-04 20:56:19 +00:00
}
}
2014-09-26 19:23:43 +00:00
void Network : : setEnabled ( bool enabled )
2013-10-07 21:00:53 +00:00
{
2014-09-26 19:23:43 +00:00
Mutex : : Lock _l ( _lock ) ;
_enabled = enabled ;
if ( _tap )
_tap - > setEnabled ( enabled ) ;
}
2013-10-07 21:00:53 +00:00
2014-09-26 19:23:43 +00:00
void Network : : destroy ( )
{
Mutex : : Lock _l ( _lock ) ;
2013-10-07 21:00:53 +00:00
2014-09-26 19:23:43 +00:00
_enabled = false ;
_destroyed = true ;
if ( _setupThread )
Thread : : join ( _setupThread ) ;
_setupThread = Thread ( ) ;
if ( _tap )
RR - > tapFactory - > close ( _tap , true ) ;
_tap = ( EthernetTap * ) 0 ;
2013-10-07 21:00:53 +00:00
}
2014-09-26 19:23:43 +00:00
void Network : : _dumpMembershipCerts ( )
2013-10-16 21:47:26 +00:00
{
2015-04-02 02:09:18 +00:00
char n [ 128 ] ;
std : : string buf ( " ZTMCD0 " ) ;
2013-10-16 21:47:26 +00:00
2015-04-02 02:09:18 +00:00
Utils : : snprintf ( n , sizeof ( n ) , " networks.d/%.16llx.mcerts " , _id ) ;
2013-10-18 16:01:48 +00:00
2015-04-02 02:09:18 +00:00
Mutex : : Lock _l ( _lock ) ;
2014-10-14 19:37:35 +00:00
2015-04-02 02:09:18 +00:00
if ( ( ! _config ) | | ( _config . isPublic ( ) ) | | ( _membershipCertificates . size ( ) = = 0 ) ) {
RR - > node - > dataStoreDelete ( n ) ;
2013-10-16 21:47:26 +00:00
return ;
}
2015-04-02 02:09:18 +00:00
for ( std : : map < Address , CertificateOfMembership > : : iterator c ( _membershipCertificates . begin ( ) ) ; c ! = _membershipCertificates . end ( ) ; + + c )
c - > second . serialize2 ( buf ) ;
2013-10-16 21:47:26 +00:00
2015-04-02 02:09:18 +00:00
RR - > node - > dataStorePut ( n , buf , true ) ;
2013-08-06 14:15:05 +00:00
}
2013-07-04 20:56:19 +00:00
} // namespace ZeroTier