2018-08-31 14:58:15 -07:00
/*
2019-08-23 09:23:39 -07:00
* Copyright (c)2019 ZeroTier, Inc.
2018-08-31 14:58:15 -07:00
*
2019-08-23 09:23:39 -07:00
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
2018-08-31 14:58:15 -07:00
*
2020-08-20 12:51:39 -07:00
* Change Date: 2025-01-01
2018-08-31 14:58:15 -07:00
*
2019-08-23 09:23:39 -07:00
* 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.
2018-08-31 14:58:15 -07:00
*/
2019-08-23 09:23:39 -07:00
/****/
2018-08-31 14:58:15 -07:00
2019-08-06 07:51:50 -05:00
#include "PostgreSQL.hpp"
2018-08-31 14:58:15 -07:00
#ifdef ZT_CONTROLLER_USE_LIBPQ
2019-08-06 07:51:50 -05:00
#include "../node/Constants.hpp"
2021-05-28 17:08:24 -04:00
#include "../node/SHA512.hpp"
2018-08-31 14:58:15 -07:00
#include "EmbeddedNetworkController.hpp"
#include "../version.h"
2020-05-11 15:03:56 -07:00
#include "Redis.hpp"
2018-08-31 14:58:15 -07:00
2018-11-30 10:37:27 -08:00
#include <libpq-fe.h>
2018-09-28 10:55:39 -07:00
#include <sstream>
2020-05-20 16:54:18 -07:00
#include <climits>
2021-08-19 17:55:30 -07:00
#include <chrono>
2020-05-11 12:29:06 -07:00
2018-08-31 14:58:15 -07:00
2021-09-02 16:22:52 -07:00
// #define ZT_TRACE 1
2021-06-05 13:44:45 -07:00
2018-08-31 14:58:15 -07:00
using json = nlohmann :: json ;
2019-08-06 07:51:50 -05:00
2018-08-31 14:58:15 -07:00
namespace {
2021-06-03 14:38:26 -07:00
static const int DB_MINIMUM_VERSION = 20 ;
2019-07-16 12:15:38 -07:00
2018-08-31 14:58:15 -07:00
static const char * _timestr ()
{
2021-06-02 13:46:54 -07:00
2018-08-31 14:58:15 -07:00
time_t t = time ( 0 );
char * ts = ctime ( & t );
char * p = ts ;
if ( ! p )
return "" ;
while ( * p ) {
if ( * p == '\n' ) {
* p = ( char ) 0 ;
break ;
}
++ p ;
}
return ts ;
}
2019-08-06 11:00:35 -05:00
/*
2018-09-06 15:14:16 -07:00
std::string join(const std::vector<std::string> &elements, const char * const separator)
{
switch(elements.size()) {
case 0:
return "";
case 1:
return elements[0];
default:
std::ostringstream os;
std::copy(elements.begin(), elements.end()-1, std::ostream_iterator<std::string>(os, separator));
os << *elements.rbegin();
return os.str();
}
}
2019-08-06 11:00:35 -05:00
*/
2018-09-06 15:14:16 -07:00
2021-08-20 10:27:45 -07:00
std :: vector < std :: string > split ( std :: string str , char delim ){
std :: istringstream iss ( str );
std :: vector < std :: string > tokens ;
std :: string item ;
while ( std :: getline ( iss , item , delim )) {
tokens . push_back ( item );
}
return tokens ;
}
2019-08-06 07:51:50 -05:00
} // anonymous namespace
2018-08-31 14:58:15 -07:00
using namespace ZeroTier ;
2021-06-02 11:44:00 -07:00
MemberNotificationReceiver :: MemberNotificationReceiver ( PostgreSQL * p , pqxx :: connection & c , const std :: string & channel )
: pqxx :: notification_receiver ( c , channel )
, _psql ( p )
2021-06-02 13:46:54 -07:00
{
fprintf ( stderr , "initialize MemberNotificaitonReceiver \n " );
}
2021-06-02 11:44:00 -07:00
void MemberNotificationReceiver :: operator () ( const std :: string & payload , int packend_pid ) {
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "Member Notification received: %s \n " , payload . c_str ());
2021-06-02 11:44:00 -07:00
json tmp ( json :: parse ( payload ));
json & ov = tmp [ "old_val" ];
json & nv = tmp [ "new_val" ];
json oldConfig , newConfig ;
if ( ov . is_object ()) oldConfig = ov ;
if ( nv . is_object ()) newConfig = nv ;
if ( oldConfig . is_object () || newConfig . is_object ()) {
_psql -> _memberChanged ( oldConfig , newConfig ,( _psql -> _ready >= 2 ));
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "payload sent \n " );
2021-06-02 11:44:00 -07:00
}
}
NetworkNotificationReceiver :: NetworkNotificationReceiver ( PostgreSQL * p , pqxx :: connection & c , const std :: string & channel )
: pqxx :: notification_receiver ( c , channel )
, _psql ( p )
2021-06-02 13:46:54 -07:00
{
fprintf ( stderr , "initialize NetworkNotificationReceiver \n " );
}
2021-06-02 11:44:00 -07:00
void NetworkNotificationReceiver :: operator () ( const std :: string & payload , int packend_pid ) {
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "Network Notificaiton received: %s \n " , payload . c_str ());
json tmp ( json :: parse ( payload ));
2021-06-02 11:44:00 -07:00
json & ov = tmp [ "old_val" ];
json & nv = tmp [ "new_val" ];
json oldConfig , newConfig ;
if ( ov . is_object ()) oldConfig = ov ;
if ( nv . is_object ()) newConfig = nv ;
if ( oldConfig . is_object () || newConfig . is_object ()) {
_psql -> _networkChanged ( oldConfig , newConfig ,( _psql -> _ready >= 2 ));
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "payload sent \n " );
2021-06-02 11:44:00 -07:00
}
}
2020-05-12 11:56:19 -07:00
using Attrs = std :: vector < std :: pair < std :: string , std :: string >> ;
using Item = std :: pair < std :: string , Attrs > ;
using ItemStream = std :: vector < Item > ;
2020-05-11 15:03:56 -07:00
PostgreSQL :: PostgreSQL ( const Identity & myId , const char * path , int listenPort , RedisConfig * rc )
2019-08-06 10:42:54 -05:00
: DB ()
2021-06-02 11:44:00 -07:00
, _pool ()
2019-08-06 10:42:54 -05:00
, _myId ( myId )
, _myAddress ( myId . address ())
2019-08-06 07:51:50 -05:00
, _ready ( 0 )
2018-08-31 14:58:15 -07:00
, _connected ( 1 )
2019-08-06 07:51:50 -05:00
, _run ( 1 )
, _waitNoticePrinted ( false )
2019-01-21 11:18:20 -08:00
, _listenPort ( listenPort )
2020-05-11 15:03:56 -07:00
, _rc ( rc )
2020-05-11 16:02:49 -07:00
, _redis ( NULL )
, _cluster ( NULL )
2018-08-31 14:58:15 -07:00
{
2019-08-06 10:42:54 -05:00
char myAddress [ 64 ];
_myAddressStr = myId . address (). toString ( myAddress );
2021-09-02 11:23:45 -07:00
_connString = std :: string ( path );
2021-06-02 11:44:00 -07:00
auto f = std :: make_shared < PostgresConnFactory > ( _connString );
_pool = std :: make_shared < ConnectionPool < PostgresConnection > > (
15 , 5 , std :: static_pointer_cast < ConnectionFactory > ( f ));
2021-05-28 17:08:24 -04:00
memset ( _ssoPsk , 0 , sizeof ( _ssoPsk ));
char * const ssoPskHex = getenv ( "ZT_SSO_PSK" );
2021-09-15 15:27:19 -07:00
#ifdef ZT_TRACE
2021-06-04 20:04:01 -07:00
fprintf ( stderr , "ZT_SSO_PSK: %s \n " , ssoPskHex );
2021-09-15 15:27:19 -07:00
#endif
2021-05-28 17:08:24 -04:00
if ( ssoPskHex ) {
// SECURITY: note that ssoPskHex will always be null-terminated if libc acatually
// returns something non-NULL. If the hex encodes something shorter than 48 bytes,
// it will be padded at the end with zeroes. If longer, it'll be truncated.
Utils :: unhex ( ssoPskHex , _ssoPsk , sizeof ( _ssoPsk ));
}
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
pqxx :: work txn { * c -> c };
2019-07-16 12:15:38 -07:00
2021-06-02 11:44:00 -07:00
pqxx :: row r { txn . exec1 ( "SELECT version FROM ztc_database" )};
int dbVersion = r [ 0 ]. as < int > ();
txn . commit ();
2019-07-16 12:15:38 -07:00
if ( dbVersion < DB_MINIMUM_VERSION ) {
fprintf ( stderr , "Central database schema version too low. This controller version requires a minimum schema version of %d. Please upgrade your Central instance" , DB_MINIMUM_VERSION );
exit ( 1 );
}
2021-06-02 11:44:00 -07:00
_pool -> unborrow ( c );
2019-07-16 12:15:38 -07:00
2020-05-11 16:02:49 -07:00
if ( _rc != NULL ) {
sw :: redis :: ConnectionOptions opts ;
sw :: redis :: ConnectionPoolOptions poolOpts ;
opts . host = _rc -> hostname ;
opts . port = _rc -> port ;
opts . password = _rc -> password ;
opts . db = 0 ;
poolOpts . size = 10 ;
if ( _rc -> clusterMode ) {
2020-05-13 09:46:41 -07:00
fprintf ( stderr , "Using Redis in Cluster Mode \n " );
2020-05-12 11:56:19 -07:00
_cluster = std :: make_shared < sw :: redis :: RedisCluster > ( opts , poolOpts );
2020-05-11 16:02:49 -07:00
} else {
2020-05-13 09:46:41 -07:00
fprintf ( stderr , "Using Redis in Standalone Mode \n " );
2020-05-12 11:56:19 -07:00
_redis = std :: make_shared < sw :: redis :: Redis > ( opts , poolOpts );
2020-05-11 16:02:49 -07:00
}
}
2018-08-31 14:58:15 -07:00
_readyLock . lock ();
2020-05-20 16:28:28 -07:00
fprintf ( stderr , "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S , :: _timestr (), ( unsigned long long ) _myAddress . toInt ());
_waitNoticePrinted = true ;
2021-06-02 11:44:00 -07:00
initializeNetworks ();
initializeMembers ();
2020-05-20 16:28:28 -07:00
2018-08-31 14:58:15 -07:00
_heartbeatThread = std :: thread ( & PostgreSQL :: heartbeat , this );
_membersDbWatcher = std :: thread ( & PostgreSQL :: membersDbWatcher , this );
_networksDbWatcher = std :: thread ( & PostgreSQL :: networksDbWatcher , this );
2018-12-06 13:08:31 -08:00
for ( int i = 0 ; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS ; ++ i ) {
2018-08-31 14:58:15 -07:00
_commitThread [ i ] = std :: thread ( & PostgreSQL :: commitThread , this );
}
_onlineNotificationThread = std :: thread ( & PostgreSQL :: onlineNotificationThread , this );
}
PostgreSQL ::~ PostgreSQL ()
{
_run = 0 ;
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 100 ));
2019-08-23 09:23:39 -07:00
2018-08-31 14:58:15 -07:00
_heartbeatThread . join ();
_membersDbWatcher . join ();
_networksDbWatcher . join ();
2020-05-12 11:56:19 -07:00
_commitQueue . stop ();
2018-12-06 13:08:31 -08:00
for ( int i = 0 ; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS ; ++ i ) {
2018-08-31 14:58:15 -07:00
_commitThread [ i ]. join ();
}
_onlineNotificationThread . join ();
}
bool PostgreSQL :: waitForReady ()
{
while ( _ready < 2 ) {
_readyLock . lock ();
_readyLock . unlock ();
}
return true ;
}
bool PostgreSQL :: isReady ()
{
return (( _ready == 2 ) && ( _connected ));
}
2019-08-06 10:42:54 -05:00
bool PostgreSQL :: save ( nlohmann :: json & record , bool notifyListeners )
2018-08-31 14:58:15 -07:00
{
2019-08-06 10:42:54 -05:00
bool modified = false ;
2018-10-24 12:06:17 -07:00
try {
2021-06-05 13:44:45 -07:00
if ( ! record . is_object ()) {
fprintf ( stderr , "record is not an object?!? \n " );
2019-08-06 11:00:35 -05:00
return false ;
2021-06-05 13:44:45 -07:00
}
2019-08-06 08:51:23 -05:00
const std :: string objtype = record [ "objtype" ];
if ( objtype == "network" ) {
2021-08-19 09:21:52 -07:00
//fprintf(stderr, "network save\n");
2019-08-06 08:51:23 -05:00
const uint64_t nwid = OSUtils :: jsonIntHex ( record [ "id" ], 0ULL );
if ( nwid ) {
nlohmann :: json old ;
get ( nwid , old );
2019-08-08 15:29:13 -05:00
if (( ! old . is_object ()) || ( ! _compareRecords ( old , record ))) {
2019-08-06 08:51:23 -05:00
record [ "revision" ] = OSUtils :: jsonInt ( record [ "revision" ], 0ULL ) + 1ULL ;
2019-08-06 10:42:54 -05:00
_commitQueue . post ( std :: pair < nlohmann :: json , bool > ( record , notifyListeners ));
modified = true ;
2019-08-06 08:51:23 -05:00
}
}
} else if ( objtype == "member" ) {
2021-06-05 13:44:07 -07:00
std :: string networkId = record [ "nwid" ];
std :: string memberId = record [ "id" ];
2019-08-06 08:51:23 -05:00
const uint64_t nwid = OSUtils :: jsonIntHex ( record [ "nwid" ], 0ULL );
const uint64_t id = OSUtils :: jsonIntHex ( record [ "id" ], 0ULL );
2021-08-19 09:21:52 -07:00
//fprintf(stderr, "member save %s-%s\n", networkId.c_str(), memberId.c_str());
2019-08-06 08:51:23 -05:00
if (( id ) && ( nwid )) {
nlohmann :: json network , old ;
get ( nwid , network , id , old );
2019-08-08 15:29:13 -05:00
if (( ! old . is_object ()) || ( ! _compareRecords ( old , record ))) {
2021-08-19 09:21:52 -07:00
//fprintf(stderr, "commit queue post\n");
2019-08-06 08:51:23 -05:00
record [ "revision" ] = OSUtils :: jsonInt ( record [ "revision" ], 0ULL ) + 1ULL ;
2019-08-06 10:42:54 -05:00
_commitQueue . post ( std :: pair < nlohmann :: json , bool > ( record , notifyListeners ));
modified = true ;
2021-06-05 13:44:07 -07:00
} else {
2021-08-19 09:21:52 -07:00
//fprintf(stderr, "no change\n");
2019-08-06 08:51:23 -05:00
}
}
2021-06-05 13:44:07 -07:00
} else {
fprintf ( stderr , "uhh waaat \n " );
2018-10-24 12:06:17 -07:00
}
} catch ( std :: exception & e ) {
fprintf ( stderr , "Error on PostgreSQL::save: %s \n " , e . what ());
} catch (...) {
fprintf ( stderr , "Unknown error on PostgreSQL::save \n " );
2018-08-31 14:58:15 -07:00
}
2019-08-06 10:42:54 -05:00
return modified ;
2018-08-31 14:58:15 -07:00
}
void PostgreSQL :: eraseNetwork ( const uint64_t networkId )
{
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "PostgreSQL::eraseNetwork \n " );
2018-08-31 14:58:15 -07:00
char tmp2 [ 24 ];
waitForReady ();
Utils :: hex ( networkId , tmp2 );
2019-08-06 10:42:54 -05:00
std :: pair < nlohmann :: json , bool > tmp ;
tmp . first [ "id" ] = tmp2 ;
tmp . first [ "objtype" ] = "_delete_network" ;
tmp . second = true ;
2018-08-31 14:58:15 -07:00
_commitQueue . post ( tmp );
2020-05-13 17:23:27 -07:00
nlohmann :: json nullJson ;
_networkChanged ( tmp . first , nullJson , true );
2018-08-31 14:58:15 -07:00
}
2019-08-23 09:23:39 -07:00
void PostgreSQL :: eraseMember ( const uint64_t networkId , const uint64_t memberId )
2018-08-31 14:58:15 -07:00
{
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "PostgreSQL::eraseMember \n " );
2018-08-31 14:58:15 -07:00
char tmp2 [ 24 ];
2020-05-20 16:28:28 -07:00
waitForReady ();
2020-05-13 17:23:27 -07:00
std :: pair < nlohmann :: json , bool > tmp , nw ;
2018-08-31 14:58:15 -07:00
Utils :: hex ( networkId , tmp2 );
2019-08-06 10:42:54 -05:00
tmp . first [ "nwid" ] = tmp2 ;
2018-08-31 14:58:15 -07:00
Utils :: hex ( memberId , tmp2 );
2019-08-06 10:42:54 -05:00
tmp . first [ "id" ] = tmp2 ;
tmp . first [ "objtype" ] = "_delete_member" ;
tmp . second = true ;
2018-08-31 14:58:15 -07:00
_commitQueue . post ( tmp );
2020-05-13 17:23:27 -07:00
nlohmann :: json nullJson ;
_memberChanged ( tmp . first , nullJson , true );
2018-08-31 14:58:15 -07:00
}
void PostgreSQL :: nodeIsOnline ( const uint64_t networkId , const uint64_t memberId , const InetAddress & physicalAddress )
{
2019-08-06 10:42:54 -05:00
std :: lock_guard < std :: mutex > l ( _lastOnline_l );
std :: pair < int64_t , InetAddress > & i = _lastOnline [ std :: pair < uint64_t , uint64_t > ( networkId , memberId )];
i . first = OSUtils :: now ();
if ( physicalAddress ) {
i . second = physicalAddress ;
2018-08-31 14:58:15 -07:00
}
}
2021-06-04 16:29:03 -07:00
std :: string PostgreSQL :: getSSOAuthURL ( const nlohmann :: json & member , const std :: string & redirectURL )
2021-05-28 17:08:24 -04:00
{
2021-06-03 14:38:26 -07:00
// NONCE is just a random character string. no semantic meaning
// state = HMAC SHA384 of Nonce based on shared sso key
//
// need nonce timeout in database? make sure it's used within X time
// X is 5 minutes for now. Make configurable later?
//
// how do we tell when a nonce is used? if auth_expiration_time is set
std :: string networkId = member [ "nwid" ];
std :: string memberId = member [ "id" ];
char authenticationURL [ 4096 ] = { 0 };
2021-05-28 17:08:24 -04:00
2021-08-19 13:03:56 -07:00
//fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str());
2021-05-28 17:08:24 -04:00
bool have_auth = false ;
try {
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
pqxx :: work w ( * c -> c );
2021-05-28 17:08:24 -04:00
2021-06-04 20:04:01 -07:00
char nonceBytes [ 16 ] = { 0 };
2021-06-03 14:38:26 -07:00
std :: string nonce = "" ;
2021-06-04 12:49:26 -07:00
// check if the member exists first.
2021-09-01 12:24:57 -07:00
pqxx :: row count = w . exec_params1 ( "SELECT count(id) FROM ztc_member WHERE id = $1 AND network_id = $2 AND deleted = false" , memberId , networkId );
2021-06-04 12:49:26 -07:00
if ( count [ 0 ]. as < int > () == 1 ) {
// find an unused nonce, if one exists.
pqxx :: result r = w . exec_params ( "SELECT nonce FROM ztc_sso_expiry "
"WHERE network_id = $1 AND member_id = $2 "
"AND authentication_expiry_time IS NULL AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)" ,
networkId , memberId );
2021-06-03 14:38:26 -07:00
2021-06-04 12:49:26 -07:00
if ( r . size () == 1 ) {
// we have an existing nonce. Use it
nonce = r . at ( 0 )[ 0 ]. as < std :: string > ();
2021-06-04 20:04:01 -07:00
Utils :: unhex ( nonce . c_str (), nonceBytes , sizeof ( nonceBytes ));
2021-06-04 12:49:26 -07:00
} else if ( r . empty ()) {
// create a nonce
2021-06-04 20:04:01 -07:00
Utils :: getSecureRandom ( nonceBytes , 16 );
char nonceBuf [ 64 ] = { 0 };
Utils :: hex ( nonceBytes , sizeof ( nonceBytes ), nonceBuf );
2021-06-04 12:49:26 -07:00
nonce = std :: string ( nonceBuf );
2021-06-03 14:38:26 -07:00
2021-06-04 12:49:26 -07:00
pqxx :: result ir = w . exec_params0 ( "INSERT INTO ztc_sso_expiry "
"(nonce, nonce_expiration, network_id, member_id) VALUES "
"($1, TO_TIMESTAMP($2::double precision/1000), $3, $4)" ,
nonce , OSUtils :: now () + 300000 , networkId , memberId );
2021-06-04 13:20:03 -07:00
w . commit ();
2021-06-04 12:49:26 -07:00
} else {
// > 1 ?!? Thats an error!
fprintf ( stderr , "> 1 unused nonce! \n " );
exit ( 6 );
}
r = w . exec_params ( "SELECT org.client_id, org.authorization_endpoint "
"FROM ztc_network AS nw, ztc_org AS org "
"WHERE nw.id = $1 AND nw.sso_enabled = true AND org.owner_id = nw.owner_id" , networkId );
std :: string client_id = "" ;
std :: string authorization_endpoint = "" ;
if ( r . size () == 1 ) {
client_id = r . at ( 0 )[ 0 ]. as < std :: string > ();
authorization_endpoint = r . at ( 0 )[ 1 ]. as < std :: string > ();
} else if ( r . size () > 1 ) {
fprintf ( stderr , "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s \n " , networkId . c_str ());
2021-06-04 13:20:03 -07:00
} else {
fprintf ( stderr , "No client or auth endpoint?!? \n " );
2021-06-04 12:49:26 -07:00
}
2021-06-04 13:20:03 -07:00
// no catch all else because we don't actually care if no records exist here. just continue as normal.
2021-06-04 12:49:26 -07:00
if (( ! client_id . empty ()) && ( ! authorization_endpoint . empty ())) {
have_auth = true ;
uint8_t state [ 48 ];
2021-06-04 20:04:01 -07:00
HMACSHA384 ( _ssoPsk , nonceBytes , sizeof ( nonceBytes ), state );
2021-06-04 12:49:26 -07:00
char state_hex [ 256 ];
Utils :: hex ( state , 48 , state_hex );
OSUtils :: ztsnprintf ( authenticationURL , sizeof ( authenticationURL ),
2021-06-04 16:56:28 -07:00
"%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s" ,
2021-06-04 12:49:26 -07:00
authorization_endpoint . c_str (),
2021-06-04 16:29:03 -07:00
redirectURL . c_str (),
2021-06-04 12:49:26 -07:00
nonce . c_str (),
state_hex ,
client_id . c_str ());
2021-06-04 13:20:03 -07:00
} else {
fprintf ( stderr , "client_id: %s \n authorization_endpoint: %s \n " , client_id . c_str (), authorization_endpoint . c_str ());
}
2021-06-03 14:38:26 -07:00
}
2021-06-02 11:44:00 -07:00
_pool -> unborrow ( c );
2021-05-28 17:08:24 -04:00
} catch ( std :: exception & e ) {
fprintf ( stderr , "ERROR: Error updating member on load: %s \n " , e . what ());
}
2021-06-03 14:38:26 -07:00
return std :: string ( authenticationURL );
2021-05-28 17:08:24 -04:00
}
2021-06-02 11:44:00 -07:00
void PostgreSQL :: initializeNetworks ()
2018-08-31 14:58:15 -07:00
{
2018-09-05 11:49:07 -07:00
try {
2020-05-26 18:54:27 -07:00
std :: string setKey = "networks:{" + _myAddressStr + "}" ;
2020-05-20 16:28:28 -07:00
fprintf ( stderr , "Initializing Networks... \n " );
2021-08-19 12:44:02 -07:00
char qbuf [ 2048 ] = { 0 };
2021-08-19 17:55:30 -07:00
sprintf ( qbuf , "SELECT n.id, (EXTRACT(EPOCH FROM n.creation_time AT TIME ZONE 'UTC')*1000)::bigint as creation_time, n.capabilities, "
"n.enable_broadcast, (EXTRACT(EPOCH FROM n.last_modified AT TIME ZONE 'UTC')*1000)::bigint AS last_modified, n.mtu, n.multicast_limit, n.name, n.private, n.remote_trace_level, "
"n.remote_trace_target, n.revision, n.rules, n.tags, n.v4_assign_mode, n.v6_assign_mode, n.sso_enabled, (CASE WHEN n.sso_enabled THEN o.client_id ELSE NULL END) as client_id, "
2021-08-20 10:27:45 -07:00
"(CASE WHEN n.sso_enabled THEN o.authorization_endpoint ELSE NULL END) as authorization_endpoint, d.domain, d.servers, "
"ARRAY(SELECT CONCAT(host(ip_range_start),'|', host(ip_range_end)) FROM ztc_network_assignment_pool WHERE network_id = n.id) AS assignment_pool, "
"ARRAY(SELECT CONCAT(host(address),'/',bits::text,'|',COALESCE(host(via), 'NULL'))FROM ztc_network_route WHERE network_id = n.id) AS routes "
2021-08-19 17:55:30 -07:00
"FROM ztc_network n "
"LEFT OUTER JOIN ztc_org o "
" ON o.owner_id = n.owner_id "
"LEFT OUTER JOIN ztc_network_dns d "
" ON d.network_id = n.id "
2021-08-19 12:44:02 -07:00
"WHERE deleted = false AND controller_id = '%s'" , _myAddressStr . c_str ());
auto c = _pool -> borrow ();
auto c2 = _pool -> borrow ();
pqxx :: work w { * c -> c };
auto stream = pqxx :: stream_from :: query ( w , qbuf );
2019-08-23 09:23:39 -07:00
2021-08-19 12:44:02 -07:00
std :: tuple <
std :: string // network ID
, std :: optional < int64_t > // creationTime
, std :: optional < std :: string > // capabilities
, std :: optional < bool > // enableBroadcast
, std :: optional < uint64_t > // lastModified
, std :: optional < int > // mtu
, std :: optional < int > // multicastLimit
, std :: optional < std :: string > // name
, bool // private
, std :: optional < int > // remoteTraceLevel
, std :: optional < std :: string > // remoteTraceTarget
, std :: optional < uint64_t > // revision
, std :: optional < std :: string > // rules
, std :: optional < std :: string > // tags
, std :: optional < std :: string > // v4AssignMode
, std :: optional < std :: string > // v6AssignMode
, std :: optional < bool > // ssoEnabled
2021-08-19 17:55:30 -07:00
, std :: optional < std :: string > // clientId
, std :: optional < std :: string > // authorizationEndpoint
, std :: optional < std :: string > // domain
, std :: optional < std :: string > // servers
2021-08-20 10:27:45 -07:00
, std :: string // assignmentPoolString
, std :: string // routeString
2021-08-19 12:44:02 -07:00
> row ;
2021-08-19 17:55:30 -07:00
uint64_t count = 0 ;
auto tmp = std :: chrono :: high_resolution_clock :: now ();
uint64_t total = 0 ;
2021-08-19 12:44:02 -07:00
while ( stream >> row ) {
2021-08-19 17:55:30 -07:00
auto start = std :: chrono :: high_resolution_clock :: now ();
2018-09-05 11:49:07 -07:00
json empty ;
json config ;
2018-09-05 16:54:23 -07:00
2021-06-03 14:38:26 -07:00
initNetwork ( config );
2021-08-19 12:44:02 -07:00
std :: string nwid = std :: get < 0 > ( row );
std :: optional < int64_t > creationTime = std :: get < 1 > ( row );
std :: optional < std :: string > capabilities = std :: get < 2 > ( row );
std :: optional < bool > enableBroadcast = std :: get < 3 > ( row );
std :: optional < uint64_t > lastModified = std :: get < 4 > ( row );
std :: optional < int > mtu = std :: get < 5 > ( row );
std :: optional < int > multicastLimit = std :: get < 6 > ( row );
std :: optional < std :: string > name = std :: get < 7 > ( row );
bool isPrivate = std :: get < 8 > ( row );
std :: optional < int > remoteTraceLevel = std :: get < 9 > ( row );
std :: optional < std :: string > remoteTraceTarget = std :: get < 10 > ( row );
std :: optional < uint64_t > revision = std :: get < 11 > ( row );
std :: optional < std :: string > rules = std :: get < 12 > ( row );
std :: optional < std :: string > tags = std :: get < 13 > ( row );
std :: optional < std :: string > v4AssignMode = std :: get < 14 > ( row );
std :: optional < std :: string > v6AssignMode = std :: get < 15 > ( row );
std :: optional < bool > ssoEnabled = std :: get < 16 > ( row );
2021-08-19 17:55:30 -07:00
std :: optional < std :: string > clientId = std :: get < 17 > ( row );
std :: optional < std :: string > authorizationEndpoint = std :: get < 18 > ( row );
std :: optional < std :: string > dnsDomain = std :: get < 19 > ( row );
std :: optional < std :: string > dnsServers = std :: get < 20 > ( row );
2021-08-20 10:27:45 -07:00
std :: string assignmentPoolString = std :: get < 21 > ( row );
std :: string routesString = std :: get < 22 > ( row );
2020-05-20 16:28:28 -07:00
2021-08-19 12:44:02 -07:00
config [ "id" ] = nwid ;
config [ "nwid" ] = nwid ;
config [ "creationTime" ] = creationTime . value_or ( 0 );
config [ "capabilities" ] = json :: parse ( capabilities . value_or ( "[]" ));
config [ "enableBroadcast" ] = enableBroadcast . value_or ( false );
config [ "lastModified" ] = lastModified . value_or ( 0 );
config [ "mtu" ] = mtu . value_or ( 2800 );
config [ "multicastLimit" ] = multicastLimit . value_or ( 64 );
config [ "name" ] = name . value_or ( "" );
config [ "private" ] = isPrivate ;
config [ "remoteTraceLevel" ] = remoteTraceLevel . value_or ( 0 );
config [ "remoteTraceTarget" ] = remoteTraceTarget . value_or ( "" );
config [ "revision" ] = revision . value_or ( 0 );
config [ "rules" ] = json :: parse ( rules . value_or ( "[]" ));
config [ "tags" ] = json :: parse ( tags . value_or ( "[]" ));
config [ "v4AssignMode" ] = json :: parse ( v4AssignMode . value_or ( "{}" ));
config [ "v6AssignMode" ] = json :: parse ( v6AssignMode . value_or ( "{}" ));
config [ "ssoEnabled" ] = ssoEnabled . value_or ( false );
config [ "objtype" ] = "network" ;
config [ "ipAssignmentPools" ] = json :: array ();
config [ "routes" ] = json :: array ();
2021-08-19 17:55:30 -07:00
config [ "clientId" ] = clientId . value_or ( "" );
config [ "authorizationEndpoint" ] = authorizationEndpoint . value_or ( "" );
if ( dnsDomain . has_value ()) {
std :: string serverList = dnsServers . value ();
json obj ;
auto servers = json :: array ();
if ( serverList . rfind ( "{" , 0 ) != std :: string :: npos ) {
serverList = serverList . substr ( 1 , serverList . size () - 2 );
std :: stringstream ss ( serverList );
while ( ss . good ()) {
std :: string server ;
std :: getline ( ss , server , ',' );
servers . push_back ( server );
}
}
obj [ "domain" ] = dnsDomain . value ();
obj [ "servers" ] = servers ;
config [ "dns" ] = obj ;
}
2021-06-02 13:46:54 -07:00
2021-08-20 10:27:45 -07:00
config [ "ipAssignmentPools" ] = json :: array ();
if ( assignmentPoolString != "{}" ) {
std :: string tmp = assignmentPoolString . substr ( 1 , assignmentPoolString . size () - 2 );
std :: vector < std :: string > assignmentPools = split ( tmp , ',' );
for ( auto it = assignmentPools . begin (); it != assignmentPools . end (); ++ it ) {
std :: vector < std :: string > r = split ( * it , '|' );
2021-08-19 12:44:02 -07:00
json ip ;
2021-08-20 10:27:45 -07:00
ip [ "ipRangeStart" ] = r [ 0 ];
ip [ "ipRangeEnd" ] = r [ 1 ];
2021-08-19 12:44:02 -07:00
config [ "ipAssignmentPools" ]. push_back ( ip );
2018-09-05 11:49:07 -07:00
}
2021-08-20 10:27:45 -07:00
}
2018-09-05 11:49:07 -07:00
2021-08-20 10:27:45 -07:00
config [ "routes" ] = json :: array ();
if ( routesString != "{}" ) {
std :: string tmp = routesString . substr ( 1 , routesString . size () - 2 );
std :: vector < std :: string > routes = split ( tmp , ',' );
for ( auto it = routes . begin (); it != routes . end (); ++ it ) {
std :: vector < std :: string > r = split ( * it , '|' );
2021-08-19 12:44:02 -07:00
json route ;
2021-08-20 10:27:45 -07:00
route [ "target" ] = r [ 0 ];
route [ "via" ] = (( route [ "via" ] == "NULL" ) ? nullptr : r [ 1 ]);
2021-08-19 12:44:02 -07:00
config [ "routes" ]. push_back ( route );
2020-08-04 09:45:45 -07:00
}
2021-08-19 12:44:02 -07:00
}
_networkChanged ( empty , config , false );
2021-08-19 17:55:30 -07:00
auto end = std :: chrono :: high_resolution_clock :: now ();
auto dur = std :: chrono :: duration_cast < std :: chrono :: microseconds > ( end - start );;
total += dur . count ();
++ count ;
2021-09-15 09:45:10 -07:00
if ( count > 0 && count % 10000 == 0 ) {
2021-08-19 17:55:30 -07:00
fprintf ( stderr , "Averaging %llu us per network \n " , ( total / count ));
}
2018-09-04 16:05:34 -07:00
}
2021-09-15 09:45:10 -07:00
if ( count > 0 ) {
fprintf ( stderr , "Took %llu us per network to load \n " , ( total / count ));
}
2021-08-19 17:55:30 -07:00
stream . complete ();
2021-08-19 12:44:02 -07:00
2021-06-02 11:44:00 -07:00
w . commit ();
2021-08-19 12:44:02 -07:00
_pool -> unborrow ( c2 );
2021-06-02 11:44:00 -07:00
_pool -> unborrow ( c );
2020-05-26 18:54:27 -07:00
2018-09-05 11:49:07 -07:00
if ( ++ this -> _ready == 2 ) {
if ( _waitNoticePrinted ) {
fprintf ( stderr , "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S , _timestr (),( unsigned long long ) _myAddress . toInt ());
}
_readyLock . unlock ();
2018-08-31 14:58:15 -07:00
}
2020-05-20 16:28:28 -07:00
} catch ( sw :: redis :: Error & e ) {
fprintf ( stderr , "ERROR: Error initializing networks in Redis: %s \n " , e . what ());
exit ( - 1 );
2018-09-05 11:49:07 -07:00
} catch ( std :: exception & e ) {
2020-05-20 16:28:28 -07:00
fprintf ( stderr , "ERROR: Error initializing networks: %s \n " , e . what ());
2018-09-05 11:49:07 -07:00
exit ( - 1 );
2018-08-31 14:58:15 -07:00
}
}
2021-06-02 11:44:00 -07:00
void PostgreSQL :: initializeMembers ()
2018-08-31 14:58:15 -07:00
{
2021-06-04 14:40:14 -07:00
std :: string memberId ;
std :: string networkId ;
2018-09-05 11:49:07 -07:00
try {
2020-05-26 18:54:27 -07:00
std :: unordered_map < std :: string , std :: string > networkMembers ;
2020-05-20 16:28:28 -07:00
fprintf ( stderr , "Initializing Members... \n " );
2021-08-19 12:44:02 -07:00
char qbuf [ 2048 ];
sprintf ( qbuf , "SELECT m.id, m.network_id, m.active_bridge, m.authorized, m.capabilities, (EXTRACT(EPOCH FROM m.creation_time AT TIME ZONE 'UTC')*1000)::bigint, m.identity, "
2021-06-02 13:46:54 -07:00
" (EXTRACT(EPOCH FROM m.last_authorized_time AT TIME ZONE 'UTC')*1000)::bigint, "
" (EXTRACT(EPOCH FROM m.last_deauthorized_time AT TIME ZONE 'UTC')*1000)::bigint, "
2018-11-30 10:37:27 -08:00
" m.remote_trace_level, m.remote_trace_target, m.tags, m.v_major, m.v_minor, m.v_rev, m.v_proto, "
2021-08-20 10:27:45 -07:00
" m.no_auto_assign_ips, m.revision, sso_exempt, "
" (SELECT (EXTRACT(EPOCH FROM e.authentication_expiry_time)*1000)::bigint "
" FROM ztc_sso_expiry e "
" INNER JOIN ztc_network n1 "
" ON n.id = e.network_id "
" WHERE e.network_id = m.network_id AND e.member_id = m.id AND n.sso_enabled = TRUE AND e.authentication_expiry_time IS NOT NULL "
2021-08-20 10:30:37 -07:00
" ORDER BY e.authentication_expiry_time DESC LIMIT 1) AS authentication_expiry_time, "
" ARRAY(SELECT DISTINCT address FROM ztc_member_ip_assignment WHERE member_id = m.id AND network_id = m.network_id) AS assigned_addresses "
2018-09-05 11:49:07 -07:00
"FROM ztc_member m "
"INNER JOIN ztc_network n "
" ON n.id = m.network_id "
2021-08-19 12:44:02 -07:00
"WHERE n.controller_id = '%s' AND m.deleted = false" , _myAddressStr . c_str ());
auto c = _pool -> borrow ();
auto c2 = _pool -> borrow ();
pqxx :: work w { * c -> c };
auto stream = pqxx :: stream_from :: query ( w , qbuf );
2018-09-04 14:51:35 -07:00
2021-08-19 12:44:02 -07:00
std :: tuple <
std :: string // memberId
, std :: string // memberId
, std :: optional < bool > // activeBridge
, std :: optional < bool > // authorized
, std :: optional < std :: string > // capabilities
, std :: optional < uint64_t > // creationTime
, std :: optional < std :: string > // identity
, std :: optional < uint64_t > // lastAuthorizedTime
, std :: optional < uint64_t > // lastDeauthorizedTime
, std :: optional < int > // remoteTraceLevel
, std :: optional < std :: string > // remoteTraceTarget
, std :: optional < std :: string > // tags
, std :: optional < int > // vMajor
, std :: optional < int > // vMinor
, std :: optional < int > // vRev
, std :: optional < int > // vProto
, std :: optional < bool > // noAutoAssignIps
, std :: optional < uint64_t > // revision
, std :: optional < bool > // ssoExempt
2021-08-20 10:27:45 -07:00
, std :: optional < uint64_t > // authenticationExpiryTime
, std :: string // assignedAddresses
2021-08-19 12:44:02 -07:00
> row ;
2021-08-19 17:55:30 -07:00
uint64_t count = 0 ;
auto tmp = std :: chrono :: high_resolution_clock :: now ();
uint64_t total = 0 ;
2021-08-19 12:44:02 -07:00
while ( stream >> row ) {
2021-08-19 17:55:30 -07:00
auto start = std :: chrono :: high_resolution_clock :: now ();
2018-09-05 11:49:07 -07:00
json empty ;
json config ;
2021-06-04 14:40:14 -07:00
2021-06-03 14:38:26 -07:00
initMember ( config );
2021-08-19 12:44:02 -07:00
memberId = std :: get < 0 > ( row );
networkId = std :: get < 1 > ( row );
std :: optional < bool > activeBridge = std :: get < 2 > ( row );
std :: optional < bool > authorized = std :: get < 3 > ( row );
std :: optional < std :: string > capabilities = std :: get < 4 > ( row );
std :: optional < uint64_t > creationTime = std :: get < 5 > ( row );
std :: optional < std :: string > identity = std :: get < 6 > ( row );
std :: optional < uint64_t > lastAuthorizedTime = std :: get < 7 > ( row );
std :: optional < uint64_t > lastDeauthorizedTime = std :: get < 8 > ( row );
std :: optional < int > remoteTraceLevel = std :: get < 9 > ( row );
std :: optional < std :: string > remoteTraceTarget = std :: get < 10 > ( row );
std :: optional < std :: string > tags = std :: get < 11 > ( row );
std :: optional < int > vMajor = std :: get < 12 > ( row );
std :: optional < int > vMinor = std :: get < 13 > ( row );
std :: optional < int > vRev = std :: get < 14 > ( row );
std :: optional < int > vProto = std :: get < 15 > ( row );
std :: optional < bool > noAutoAssignIps = std :: get < 16 > ( row );
std :: optional < uint64_t > revision = std :: get < 17 > ( row );
std :: optional < bool > ssoExempt = std :: get < 18 > ( row );
2021-08-20 10:27:45 -07:00
std :: optional < uint64_t > authenticationExpiryTime = std :: get < 19 > ( row );
std :: string assignedAddresses = std :: get < 20 > ( row );
2020-05-20 16:28:28 -07:00
2018-09-05 11:49:07 -07:00
config [ "id" ] = memberId ;
config [ "nwid" ] = networkId ;
2021-08-19 12:44:02 -07:00
config [ "activeBridge" ] = activeBridge . value_or ( false );
config [ "authorized" ] = authorized . value_or ( false );
config [ "capabilities" ] = json :: parse ( capabilities . value_or ( "[]" ));
config [ "creationTime" ] = creationTime . value_or ( 0 );
config [ "identity" ] = identity . value_or ( "" );
config [ "lastAuthorizedTime" ] = lastAuthorizedTime . value_or ( 0 );
config [ "lastDeauthorizedTime" ] = lastDeauthorizedTime . value_or ( 0 );
config [ "remoteTraceLevel" ] = remoteTraceLevel . value_or ( 0 );
config [ "remoteTraceTarget" ] = remoteTraceTarget . value_or ( "" );
config [ "tags" ] = json :: parse ( tags . value_or ( "[]" ));
config [ "vMajor" ] = vMajor . value_or ( - 1 );
config [ "vMinor" ] = vMinor . value_or ( - 1 );
config [ "vRev" ] = vRev . value_or ( - 1 );
config [ "vProto" ] = vProto . value_or ( - 1 );
config [ "noAutoAssignIps" ] = noAutoAssignIps . value_or ( false );
config [ "revision" ] = revision . value_or ( 0 );
config [ "ssoExempt" ] = ssoExempt . value_or ( false );
2021-08-20 10:27:45 -07:00
config [ "authenticationExpiryTime" ] = authenticationExpiryTime . value_or ( 0 );
2021-08-19 17:55:30 -07:00
config [ "objtype" ] = "member" ;
2021-08-20 10:27:45 -07:00
config [ "ipAssignments" ] = json :: array ();
2021-08-19 12:44:02 -07:00
2021-08-20 10:27:45 -07:00
if ( assignedAddresses != "{}" ) {
std :: string tmp = assignedAddresses . substr ( 1 , assignedAddresses . size () - 2 );
std :: vector < std :: string > addrs = split ( tmp , ',' );
for ( auto it = addrs . begin (); it != addrs . end (); ++ it ) {
config [ "ipAssignments" ]. push_back ( * it );
2020-03-03 23:52:53 -08:00
}
2018-09-05 11:49:07 -07:00
}
_memberChanged ( empty , config , false );
2021-08-19 12:44:02 -07:00
memberId = "" ;
networkId = "" ;
2021-08-19 17:55:30 -07:00
auto end = std :: chrono :: high_resolution_clock :: now ();
auto dur = std :: chrono :: duration_cast < std :: chrono :: microseconds > ( end - start );;
total += dur . count ();
++ count ;
2021-09-15 09:45:10 -07:00
if ( count > 0 && count % 10000 == 0 ) {
2021-08-19 17:55:30 -07:00
fprintf ( stderr , "Averaging %llu us per member \n " , ( total / count ));
}
2018-09-04 14:51:35 -07:00
}
2021-09-15 09:45:10 -07:00
if ( count > 0 ) {
fprintf ( stderr , "Took %llu us per member to load \n " , ( total / count ));
}
2021-08-19 17:55:30 -07:00
stream . complete ();
2018-11-30 10:37:27 -08:00
2021-06-02 11:44:00 -07:00
w . commit ();
2021-08-19 12:44:02 -07:00
_pool -> unborrow ( c2 );
2021-06-02 11:44:00 -07:00
_pool -> unborrow ( c );
2018-09-04 14:51:35 -07:00
2018-09-05 11:49:07 -07:00
if ( ++ this -> _ready == 2 ) {
if ( _waitNoticePrinted ) {
fprintf ( stderr , "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S , _timestr (),( unsigned long long ) _myAddress . toInt ());
}
_readyLock . unlock ();
2018-08-31 14:58:15 -07:00
}
2020-05-20 16:28:28 -07:00
} catch ( sw :: redis :: Error & e ) {
fprintf ( stderr , "ERROR: Error initializing members (redis): %s \n " , e . what ());
2018-09-05 11:49:07 -07:00
} catch ( std :: exception & e ) {
2021-06-04 14:40:14 -07:00
fprintf ( stderr , "ERROR: Error initializing member: %s-%s %s \n " , networkId . c_str (), memberId . c_str (), e . what ());
2018-09-05 11:49:07 -07:00
exit ( - 1 );
2018-08-31 14:58:15 -07:00
}
}
void PostgreSQL :: heartbeat ()
{
char publicId [ 1024 ];
char hostnameTmp [ 1024 ];
_myId . toString ( false , publicId );
if ( gethostname ( hostnameTmp , sizeof ( hostnameTmp )) != 0 ) {
hostnameTmp [ 0 ] = ( char ) 0 ;
} else {
2019-08-08 11:04:11 -05:00
for ( int i = 0 ; i < ( int ) sizeof ( hostnameTmp ); ++ i ) {
2018-08-31 14:58:15 -07:00
if (( hostnameTmp [ i ] == '.' ) || ( hostnameTmp [ i ] == 0 )) {
hostnameTmp [ i ] = ( char ) 0 ;
break ;
}
}
}
const char * controllerId = _myAddressStr . c_str ();
const char * publicIdentity = publicId ;
const char * hostname = hostnameTmp ;
while ( _run == 1 ) {
2021-09-02 16:46:42 -07:00
// fprintf(stderr, "%s: heartbeat\n", controllerId);
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
2020-05-22 14:16:04 -07:00
int64_t ts = OSUtils :: now ();
2021-06-02 11:44:00 -07:00
if ( c -> c ) {
pqxx :: work w { * c -> c };
2018-11-30 10:37:27 -08:00
std :: string major = std :: to_string ( ZEROTIER_ONE_VERSION_MAJOR );
std :: string minor = std :: to_string ( ZEROTIER_ONE_VERSION_MINOR );
std :: string rev = std :: to_string ( ZEROTIER_ONE_VERSION_REVISION );
std :: string build = std :: to_string ( ZEROTIER_ONE_VERSION_BUILD );
2020-05-22 14:16:04 -07:00
std :: string now = std :: to_string ( ts );
2019-01-21 11:29:13 -08:00
std :: string host_port = std :: to_string ( _listenPort );
2021-09-02 12:45:19 -07:00
std :: string use_redis = ( _rc != NULL ) ? "true" : "false" ;
2021-06-02 11:44:00 -07:00
try {
pqxx :: result res = w . exec0 ( "INSERT INTO ztc_controller (id, cluster_host, last_alive, public_identity, v_major, v_minor, v_rev, v_build, host_port, use_redis) "
"VALUES (" + w . quote ( controllerId ) + ", " + w . quote ( hostname ) + ", TO_TIMESTAMP(" + now + "::double precision/1000), " +
w . quote ( publicIdentity ) + ", " + major + ", " + minor + ", " + rev + ", " + build + ", " + host_port + ", " + use_redis + ") "
2018-11-30 10:37:27 -08:00
"ON CONFLICT (id) DO UPDATE SET cluster_host = EXCLUDED.cluster_host, last_alive = EXCLUDED.last_alive, "
"public_identity = EXCLUDED.public_identity, v_major = EXCLUDED.v_major, v_minor = EXCLUDED.v_minor, "
2019-03-06 16:16:49 -08:00
"v_rev = EXCLUDED.v_rev, v_build = EXCLUDED.v_rev, host_port = EXCLUDED.host_port, "
2021-06-02 11:44:00 -07:00
"use_redis = EXCLUDED.use_redis" );
} catch ( std :: exception & e ) {
fprintf ( stderr , "Heartbeat update failed: %s \n " , e . what ());
w . abort ();
_pool -> unborrow ( c );
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 1000 ));
continue ;
}
w . commit ();
2018-11-30 10:37:27 -08:00
}
2021-06-02 11:44:00 -07:00
_pool -> unborrow ( c );
2018-11-30 10:37:27 -08:00
2018-08-31 14:58:15 -07:00
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 1000 ));
}
2020-05-12 11:56:19 -07:00
fprintf ( stderr , "Exited heartbeat thread \n " );
2018-08-31 14:58:15 -07:00
}
void PostgreSQL :: membersDbWatcher ()
{
2020-05-12 11:56:19 -07:00
if ( _rc ) {
_membersWatcher_Redis ();
2019-03-04 17:01:16 -08:00
} else {
2021-06-02 11:44:00 -07:00
_membersWatcher_Postgres ();
2019-03-04 17:01:16 -08:00
}
if ( _run == 1 ) {
fprintf ( stderr , "ERROR: %s membersDbWatcher should still be running! Exiting Controller. \n " , _myAddressStr . c_str ());
exit ( 9 );
}
2019-03-08 10:50:33 -08:00
fprintf ( stderr , "Exited membersDbWatcher \n " );
2019-03-04 17:01:16 -08:00
}
2021-06-02 11:44:00 -07:00
void PostgreSQL :: _membersWatcher_Postgres () {
auto c = _pool -> borrow ();
2019-03-06 16:16:49 -08:00
2021-06-02 11:44:00 -07:00
std :: string stream = "member_" + _myAddressStr ;
fprintf ( stderr , "Listening to member stream: %s \n " , stream . c_str ());
MemberNotificationReceiver m ( this , * c -> c , stream );
2019-03-06 16:16:49 -08:00
2018-11-30 10:37:27 -08:00
while ( _run == 1 ) {
2021-06-02 11:44:00 -07:00
c -> c -> await_notification ( 5 , 0 );
2018-11-30 10:37:27 -08:00
}
2021-06-02 11:44:00 -07:00
_pool -> unborrow ( c );
2019-03-04 17:01:16 -08:00
}
2020-05-12 11:56:19 -07:00
void PostgreSQL :: _membersWatcher_Redis () {
char buf [ 11 ] = { 0 };
std :: string key = "member-stream:{" + std :: string ( _myAddress . toString ( buf )) + "}" ;
2020-07-27 18:37:45 -07:00
fprintf ( stderr , "Listening to member stream: %s \n " , key . c_str ());
2020-05-12 11:56:19 -07:00
while ( _run == 1 ) {
2020-05-20 11:42:51 -07:00
try {
json tmp ;
std :: unordered_map < std :: string , ItemStream > result ;
if ( _rc -> clusterMode ) {
_cluster -> xread ( key , "$" , std :: chrono :: seconds ( 1 ), 0 , std :: inserter ( result , result . end ()));
} else {
_redis -> xread ( key , "$" , std :: chrono :: seconds ( 1 ), 0 , std :: inserter ( result , result . end ()));
}
if ( ! result . empty ()) {
for ( auto element : result ) {
#ifdef ZT_TRACE
fprintf ( stdout , "Received notification from: %s \n " , element . first . c_str ());
#endif
for ( auto rec : element . second ) {
std :: string id = rec . first ;
auto attrs = rec . second ;
#ifdef ZT_TRACE
fprintf ( stdout , "Record ID: %s \n " , id . c_str ());
fprintf ( stdout , "attrs len: %lu \n " , attrs . size ());
#endif
for ( auto a : attrs ) {
#ifdef ZT_TRACE
fprintf ( stdout , "key: %s \n value: %s \n " , a . first . c_str (), a . second . c_str ());
#endif
try {
tmp = json :: parse ( a . second );
json & ov = tmp [ "old_val" ];
json & nv = tmp [ "new_val" ];
json oldConfig , newConfig ;
if ( ov . is_object ()) oldConfig = ov ;
if ( nv . is_object ()) newConfig = nv ;
if ( oldConfig . is_object () || newConfig . is_object ()) {
_memberChanged ( oldConfig , newConfig ,( this -> _ready >= 2 ));
}
} catch (...) {
fprintf ( stderr , "json parse error in networkWatcher_Redis \n " );
2020-05-12 11:56:19 -07:00
}
}
2020-05-20 11:42:51 -07:00
if ( _rc -> clusterMode ) {
_cluster -> xdel ( key , id );
} else {
_redis -> xdel ( key , id );
}
2020-05-12 12:37:05 -07:00
}
2020-05-12 11:56:19 -07:00
}
}
2020-05-20 11:42:51 -07:00
} catch ( sw :: redis :: Error & e ) {
fprintf ( stderr , "Error in Redis members watcher: %s \n " , e . what ());
2020-05-12 11:56:19 -07:00
}
}
fprintf ( stderr , "membersWatcher ended \n " );
2020-05-11 11:48:05 -07:00
}
2018-08-31 14:58:15 -07:00
void PostgreSQL :: networksDbWatcher ()
{
2020-05-11 15:03:56 -07:00
if ( _rc ) {
_networksWatcher_Redis ();
2019-03-04 17:01:16 -08:00
} else {
2021-06-02 11:44:00 -07:00
_networksWatcher_Postgres ();
2019-03-04 17:01:16 -08:00
}
2019-08-23 09:23:39 -07:00
2019-03-04 17:01:16 -08:00
if ( _run == 1 ) {
fprintf ( stderr , "ERROR: %s networksDbWatcher should still be running! Exiting Controller. \n " , _myAddressStr . c_str ());
exit ( 8 );
}
2019-11-13 12:46:16 -08:00
fprintf ( stderr , "Exited networksDbWatcher \n " );
2019-03-04 17:01:16 -08:00
}
2021-06-02 11:44:00 -07:00
void PostgreSQL :: _networksWatcher_Postgres () {
std :: string stream = "network_" + _myAddressStr ;
2019-03-06 16:16:49 -08:00
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "Listening to member stream: %s \n " , stream . c_str ());
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
NetworkNotificationReceiver n ( this , * c -> c , stream );
2019-03-06 16:16:49 -08:00
2018-11-30 10:37:27 -08:00
while ( _run == 1 ) {
2021-06-02 11:44:00 -07:00
c -> c -> await_notification ( 5 , 0 );
2018-11-30 10:37:27 -08:00
}
2019-03-04 17:01:16 -08:00
}
2020-05-11 11:48:05 -07:00
void PostgreSQL :: _networksWatcher_Redis () {
2020-05-12 11:56:19 -07:00
char buf [ 11 ] = { 0 };
std :: string key = "network-stream:{" + std :: string ( _myAddress . toString ( buf )) + "}" ;
while ( _run == 1 ) {
2020-05-20 11:42:51 -07:00
try {
json tmp ;
std :: unordered_map < std :: string , ItemStream > result ;
if ( _rc -> clusterMode ) {
_cluster -> xread ( key , "$" , std :: chrono :: seconds ( 1 ), 0 , std :: inserter ( result , result . end ()));
} else {
_redis -> xread ( key , "$" , std :: chrono :: seconds ( 1 ), 0 , std :: inserter ( result , result . end ()));
}
if ( ! result . empty ()) {
for ( auto element : result ) {
2020-05-12 12:48:58 -07:00
#ifdef ZT_TRACE
2020-05-20 11:42:51 -07:00
fprintf ( stdout , "Received notification from: %s \n " , element . first . c_str ());
2020-05-12 12:48:58 -07:00
#endif
2020-05-20 11:42:51 -07:00
for ( auto rec : element . second ) {
std :: string id = rec . first ;
auto attrs = rec . second ;
2020-05-12 12:48:58 -07:00
#ifdef ZT_TRACE
2020-05-20 11:42:51 -07:00
fprintf ( stdout , "Record ID: %s \n " , id . c_str ());
fprintf ( stdout , "attrs len: %lu \n " , attrs . size ());
2020-05-12 12:48:58 -07:00
#endif
2020-05-20 11:42:51 -07:00
for ( auto a : attrs ) {
2020-05-12 12:48:58 -07:00
#ifdef ZT_TRACE
2020-05-20 11:42:51 -07:00
fprintf ( stdout , "key: %s \n value: %s \n " , a . first . c_str (), a . second . c_str ());
2020-05-12 12:48:58 -07:00
#endif
2020-05-20 11:42:51 -07:00
try {
tmp = json :: parse ( a . second );
json & ov = tmp [ "old_val" ];
json & nv = tmp [ "new_val" ];
json oldConfig , newConfig ;
if ( ov . is_object ()) oldConfig = ov ;
if ( nv . is_object ()) newConfig = nv ;
if ( oldConfig . is_object () || newConfig . is_object ()) {
_networkChanged ( oldConfig , newConfig ,( this -> _ready >= 2 ));
}
} catch (...) {
fprintf ( stderr , "json parse error in networkWatcher_Redis \n " );
2020-05-12 11:56:19 -07:00
}
}
2020-05-20 11:42:51 -07:00
if ( _rc -> clusterMode ) {
_cluster -> xdel ( key , id );
} else {
_redis -> xdel ( key , id );
}
2020-05-12 12:37:05 -07:00
}
2020-05-12 11:56:19 -07:00
}
}
2020-05-20 11:42:51 -07:00
} catch ( sw :: redis :: Error & e ) {
fprintf ( stderr , "Error in Redis networks watcher: %s \n " , e . what ());
2020-05-12 11:56:19 -07:00
}
}
fprintf ( stderr , "networksWatcher ended \n " );
2020-05-11 11:48:05 -07:00
}
2018-08-31 14:58:15 -07:00
void PostgreSQL :: commitThread ()
{
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s: commitThread start \n " , _myAddressStr . c_str ());
2019-08-06 10:42:54 -05:00
std :: pair < nlohmann :: json , bool > qitem ;
while ( _commitQueue . get ( qitem ) & ( _run == 1 )) {
2021-08-19 17:55:30 -07:00
//fprintf(stderr, "commitThread tick\n");
2019-08-06 10:42:54 -05:00
if ( ! qitem . first . is_object ()) {
2021-06-02 13:46:54 -07:00
fprintf ( stderr , "not an object \n " );
2018-08-31 14:58:15 -07:00
continue ;
}
2021-06-02 11:44:00 -07:00
2019-08-06 10:42:54 -05:00
try {
nlohmann :: json * config = & ( qitem . first );
2018-09-05 11:49:07 -07:00
const std :: string objtype = ( * config )[ "objtype" ];
if ( objtype == "member" ) {
2021-09-02 16:22:52 -07:00
// fprintf(stderr, "%s: commitThread: member\n", _myAddressStr.c_str());
2018-09-05 11:49:07 -07:00
try {
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
pqxx :: work w ( * c -> c );
2020-10-05 13:32:47 -07:00
std :: string memberId = ( * config )[ "id" ];
std :: string networkId = ( * config )[ "nwid" ];
std :: string target = "NULL" ;
if ( ! ( * config )[ "remoteTraceTarget" ]. is_null ()) {
target = ( * config )[ "remoteTraceTarget" ];
}
2021-06-02 11:44:00 -07:00
pqxx :: result res = w . exec_params0 (
2020-10-05 13:32:47 -07:00
"INSERT INTO ztc_member (id, network_id, active_bridge, authorized, capabilities, "
"identity, last_authorized_time, last_deauthorized_time, no_auto_assign_ips, "
"remote_trace_level, remote_trace_target, revision, tags, v_major, v_minor, v_rev, v_proto) "
"VALUES ($1, $2, $3, $4, $5, $6, "
"TO_TIMESTAMP($7::double precision/1000), TO_TIMESTAMP($8::double precision/1000), "
"$9, $10, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (network_id, id) DO UPDATE SET "
"active_bridge = EXCLUDED.active_bridge, authorized = EXCLUDED.authorized, capabilities = EXCLUDED.capabilities, "
"identity = EXCLUDED.identity, last_authorized_time = EXCLUDED.last_authorized_time, "
"last_deauthorized_time = EXCLUDED.last_deauthorized_time, no_auto_assign_ips = EXCLUDED.no_auto_assign_ips, "
"remote_trace_level = EXCLUDED.remote_trace_level, remote_trace_target = EXCLUDED.remote_trace_target, "
"revision = EXCLUDED.revision+1, tags = EXCLUDED.tags, v_major = EXCLUDED.v_major, "
"v_minor = EXCLUDED.v_minor, v_rev = EXCLUDED.v_rev, v_proto = EXCLUDED.v_proto" ,
2021-06-02 11:44:00 -07:00
memberId ,
networkId ,
( bool )( * config )[ "activeBridge" ],
( bool )( * config )[ "authorized" ],
OSUtils :: jsonDump (( * config )[ "capabilities" ], - 1 ),
2021-06-03 18:30:32 -07:00
OSUtils :: jsonString (( * config )[ "identity" ], "" ),
2021-06-02 11:44:00 -07:00
( uint64_t )( * config )[ "lastAuthorizedTime" ],
( uint64_t )( * config )[ "lastDeauthorizedTime" ],
( bool )( * config )[ "noAutoAssignIps" ],
( int )( * config )[ "remoteTraceLevel" ],
target ,
( uint64_t )( * config )[ "revision" ],
OSUtils :: jsonDump (( * config )[ "tags" ], - 1 ),
( int )( * config )[ "vMajor" ],
( int )( * config )[ "vMinor" ],
( int )( * config )[ "vRev" ],
( int )( * config )[ "vProto" ]);
2020-10-05 13:32:47 -07:00
2021-06-02 11:44:00 -07:00
res = w . exec_params0 ( "DELETE FROM ztc_member_ip_assignment WHERE member_id = $1 AND network_id = $2" ,
memberId , networkId );
2020-10-05 13:32:47 -07:00
std :: vector < std :: string > assignments ;
bool ipAssignError = false ;
for ( auto i = ( * config )[ "ipAssignments" ]. begin (); i != ( * config )[ "ipAssignments" ]. end (); ++ i ) {
std :: string addr = * i ;
if ( std :: find ( assignments . begin (), assignments . end (), addr ) != assignments . end ()) {
continue ;
2018-11-30 10:37:27 -08:00
}
2021-06-02 11:44:00 -07:00
res = w . exec_params0 (
2020-10-05 13:32:47 -07:00
"INSERT INTO ztc_member_ip_assignment (member_id, network_id, address) VALUES ($1, $2, $3) ON CONFLICT (network_id, member_id, address) DO NOTHING" ,
2021-06-02 11:44:00 -07:00
memberId , networkId , addr );
2019-08-23 09:23:39 -07:00
2020-10-05 13:32:47 -07:00
assignments . push_back ( addr );
}
if ( ipAssignError ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s: ipAssignError \n " , _myAddressStr . c_str ());
2020-10-05 13:32:47 -07:00
delete config ;
config = nullptr ;
continue ;
}
2020-10-05 11:02:40 -07:00
2021-06-02 11:44:00 -07:00
w . commit ();
_pool -> unborrow ( c );
2018-11-30 10:37:27 -08:00
const uint64_t nwidInt = OSUtils :: jsonIntHex (( * config )[ "nwid" ], 0ULL );
const uint64_t memberidInt = OSUtils :: jsonIntHex (( * config )[ "id" ], 0ULL );
if ( nwidInt && memberidInt ) {
nlohmann :: json nwOrig ;
nlohmann :: json memOrig ;
nlohmann :: json memNew ( * config );
2019-08-06 10:42:54 -05:00
2018-11-30 10:37:27 -08:00
get ( nwidInt , nwOrig , memberidInt , memOrig );
2019-08-23 09:23:39 -07:00
2019-08-06 10:42:54 -05:00
_memberChanged ( memOrig , memNew , qitem . second );
2018-11-30 10:37:27 -08:00
} else {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s: Can't notify of change. Error parsing nwid or memberid: %llu-%llu \n " , _myAddressStr . c_str (), ( unsigned long long ) nwidInt , ( unsigned long long ) memberidInt );
2018-11-30 10:37:27 -08:00
}
2018-09-05 11:49:07 -07:00
} catch ( std :: exception & e ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s ERROR: Error updating member: %s \n " , _myAddressStr . c_str (), e . what ());
2018-09-05 11:49:07 -07:00
}
} else if ( objtype == "network" ) {
try {
2021-09-02 16:22:52 -07:00
// fprintf(stderr, "%s: commitThread: network\n", _myAddressStr.c_str());
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
pqxx :: work w ( * c -> c );
2020-10-05 13:32:47 -07:00
std :: string id = ( * config )[ "id" ];
2021-06-02 11:44:00 -07:00
std :: string remoteTraceTarget = "" ;
if ( ! ( * config )[ "remoteTraceTarget" ]. is_null ()) {
2020-10-05 13:32:47 -07:00
remoteTraceTarget = ( * config )[ "remoteTraceTarget" ];
}
2021-06-02 11:44:00 -07:00
std :: string rulesSource = "" ;
2020-10-05 13:32:47 -07:00
if (( * config )[ "rulesSource" ]. is_string ()) {
rulesSource = ( * config )[ "rulesSource" ];
}
2020-10-05 11:02:40 -07:00
2020-10-05 13:32:47 -07:00
// This ugly query exists because when we want to mirror networks to/from
// another data store (e.g. FileDB or LFDB) it is possible to get a network
// that doesn't exist in Central's database. This does an upsert and sets
// the owner_id to the "first" global admin in the user DB if the record
// did not previously exist. If the record already exists owner_id is left
// unchanged, so owner_id should be left out of the update clause.
2021-06-02 11:44:00 -07:00
pqxx :: result res = w . exec_params0 (
2020-10-05 13:32:47 -07:00
"INSERT INTO ztc_network (id, creation_time, owner_id, controller_id, capabilities, enable_broadcast, "
"last_modified, mtu, multicast_limit, name, private, "
"remote_trace_level, remote_trace_target, rules, rules_source, "
2021-06-05 13:44:45 -07:00
"tags, v4_assign_mode, v6_assign_mode, sso_enabled) VALUES ("
2020-10-05 13:32:47 -07:00
"$1, TO_TIMESTAMP($5::double precision/1000), "
"(SELECT user_id AS owner_id FROM ztc_global_permissions WHERE authorize = true AND del = true AND modify = true AND read = true LIMIT 1),"
"$2, $3, $4, TO_TIMESTAMP($5::double precision/1000), "
2021-06-05 13:44:45 -07:00
"$6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, 17) "
2020-10-05 13:32:47 -07:00
"ON CONFLICT (id) DO UPDATE set controller_id = EXCLUDED.controller_id, "
"capabilities = EXCLUDED.capabilities, enable_broadcast = EXCLUDED.enable_broadcast, "
"last_modified = EXCLUDED.last_modified, mtu = EXCLUDED.mtu, "
"multicast_limit = EXCLUDED.multicast_limit, name = EXCLUDED.name, "
"private = EXCLUDED.private, remote_trace_level = EXCLUDED.remote_trace_level, "
"remote_trace_target = EXCLUDED.remote_trace_target, rules = EXCLUDED.rules, "
"rules_source = EXCLUDED.rules_source, tags = EXCLUDED.tags, "
2021-06-05 13:44:45 -07:00
"v4_assign_mode = EXCLUDED.v4_assign_mode, v6_assign_mode = EXCLUDED.v6_assign_mode, "
"sso_enabled = EXCLUDED.sso_enabled" ,
2021-06-02 11:44:00 -07:00
id ,
_myAddressStr ,
OSUtils :: jsonDump (( * config )[ "capabilitles" ], - 1 ),
( bool )( * config )[ "enableBroadcast" ],
OSUtils :: now (),
( int )( * config )[ "mtu" ],
( int )( * config )[ "multicastLimit" ],
2021-06-03 18:30:32 -07:00
OSUtils :: jsonString (( * config )[ "name" ], "" ),
2021-06-02 11:44:00 -07:00
( bool )( * config )[ "private" ],
( int )( * config )[ "remoteTraceLevel" ],
remoteTraceTarget ,
OSUtils :: jsonDump (( * config )[ "rules" ], - 1 ),
rulesSource ,
OSUtils :: jsonDump (( * config )[ "tags" ], - 1 ),
OSUtils :: jsonDump (( * config )[ "v4AssignMode" ], - 1 ),
2021-06-05 13:44:45 -07:00
OSUtils :: jsonDump (( * config )[ "v6AssignMode" ], - 1 ),
OSUtils :: jsonBool (( * config )[ "ssoEnabled" ], false ));
2020-10-05 11:02:40 -07:00
2021-06-02 11:44:00 -07:00
res = w . exec_params0 ( "DELETE FROM ztc_network_assignment_pool WHERE network_id = $1" , 0 );
2020-10-05 11:02:40 -07:00
2020-10-05 13:32:47 -07:00
auto pool = ( * config )[ "ipAssignmentPools" ];
bool err = false ;
for ( auto i = pool . begin (); i != pool . end (); ++ i ) {
std :: string start = ( * i )[ "ipRangeStart" ];
std :: string end = ( * i )[ "ipRangeEnd" ];
2020-10-05 11:02:40 -07:00
2021-06-02 11:44:00 -07:00
res = w . exec_params0 (
2020-10-05 13:32:47 -07:00
"INSERT INTO ztc_network_assignment_pool (network_id, ip_range_start, ip_range_end) "
2021-06-02 11:44:00 -07:00
"VALUES ($1, $2, $3)" , id , start , end );
2020-10-05 13:32:47 -07:00
}
2021-06-02 11:44:00 -07:00
res = w . exec_params0 ( "DELETE FROM ztc_network_route WHERE network_id = $1" , id );
2020-10-05 13:32:47 -07:00
auto routes = ( * config )[ "routes" ];
err = false ;
for ( auto i = routes . begin (); i != routes . end (); ++ i ) {
std :: string t = ( * i )[ "target" ];
std :: vector < std :: string > target ;
std :: istringstream f ( t );
std :: string s ;
while ( std :: getline ( f , s , '/' )) {
target . push_back ( s );
}
if ( target . empty () || target . size () != 2 ) {
2020-10-05 11:02:40 -07:00
continue ;
2020-08-12 13:08:47 -07:00
}
2020-10-05 13:32:47 -07:00
std :: string targetAddr = target [ 0 ];
std :: string targetBits = target [ 1 ];
std :: string via = "NULL" ;
if ( ! ( * i )[ "via" ]. is_null ()) {
via = ( * i )[ "via" ];
}
2021-06-02 11:44:00 -07:00
res = w . exec_params0 ( "INSERT INTO ztc_network_route (network_id, address, bits, via) VALUES ($1, $2, $3, $4)" ,
id , targetAddr , targetBits , ( via == "NULL" ? NULL : via . c_str ()));
2018-09-05 11:49:07 -07:00
}
2020-10-05 13:32:47 -07:00
if ( err ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s: route add error \n " , _myAddressStr . c_str ());
2021-06-02 11:44:00 -07:00
w . abort ();
_pool -> unborrow ( c );
2020-10-05 13:32:47 -07:00
delete config ;
config = nullptr ;
continue ;
}
2021-06-02 11:44:00 -07:00
2020-10-05 13:32:47 -07:00
auto dns = ( * config )[ "dns" ];
std :: string domain = dns [ "domain" ];
std :: stringstream servers ;
servers << "{" ;
for ( auto j = dns [ "servers" ]. begin (); j < dns [ "servers" ]. end (); ++ j ) {
servers << * j ;
if ( ( j + 1 ) != dns [ "servers" ]. end ()) {
servers << "," ;
}
}
servers << "}" ;
2021-06-02 11:44:00 -07:00
std :: string s = servers . str ();
2020-10-05 13:32:47 -07:00
2021-06-02 11:44:00 -07:00
res = w . exec_params0 ( "INSERT INTO ztc_network_dns (network_id, domain, servers) VALUES ($1, $2, $3) ON CONFLICT (network_id) DO UPDATE SET domain = EXCLUDED.domain, servers = EXCLUDED.servers" ,
id , domain , s );
2020-10-05 13:32:47 -07:00
2021-06-02 11:44:00 -07:00
w . commit ();
_pool -> unborrow ( c );
2018-11-30 10:37:27 -08:00
const uint64_t nwidInt = OSUtils :: jsonIntHex (( * config )[ "nwid" ], 0ULL );
if ( nwidInt ) {
nlohmann :: json nwOrig ;
nlohmann :: json nwNew ( * config );
get ( nwidInt , nwOrig );
2019-08-06 10:42:54 -05:00
_networkChanged ( nwOrig , nwNew , qitem . second );
2018-11-30 10:37:27 -08:00
} else {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s: Can't notify network changed: %llu \n " , _myAddressStr . c_str (), ( unsigned long long ) nwidInt );
2018-11-30 10:37:27 -08:00
}
2018-10-24 12:06:17 -07:00
2018-09-05 11:49:07 -07:00
} catch ( std :: exception & e ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s ERROR: Error updating network: %s \n " , _myAddressStr . c_str (), e . what ());
2018-09-05 11:49:07 -07:00
}
} else if ( objtype == "_delete_network" ) {
2021-09-02 16:22:52 -07:00
// fprintf(stderr, "%s: commitThread: delete network\n", _myAddressStr.c_str());
2018-09-05 11:49:07 -07:00
try {
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
pqxx :: work w ( * c -> c );
2018-09-05 11:49:07 -07:00
std :: string networkId = ( * config )[ "nwid" ];
2019-08-23 09:23:39 -07:00
2021-06-02 11:44:00 -07:00
pqxx :: result res = w . exec_params0 ( "UPDATE ztc_network SET deleted = true WHERE id = $1" ,
networkId );
2018-11-30 10:37:27 -08:00
2021-06-02 11:44:00 -07:00
w . commit ();
_pool -> unborrow ( c );
2018-09-05 11:49:07 -07:00
} catch ( std :: exception & e ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s ERROR: Error deleting network: %s \n " , _myAddressStr . c_str (), e . what ());
2018-09-05 11:49:07 -07:00
}
2021-06-02 11:44:00 -07:00
2018-09-05 11:49:07 -07:00
} else if ( objtype == "_delete_member" ) {
2021-09-02 16:22:52 -07:00
// fprintf(stderr, "%s commitThread: delete member\n", _myAddressStr.c_str());
2018-09-05 11:49:07 -07:00
try {
2021-06-02 11:44:00 -07:00
auto c = _pool -> borrow ();
pqxx :: work w ( * c -> c );
2018-09-05 11:49:07 -07:00
std :: string memberId = ( * config )[ "id" ];
std :: string networkId = ( * config )[ "nwid" ];
2021-06-02 11:44:00 -07:00
pqxx :: result res = w . exec_params0 (
2018-11-30 10:37:27 -08:00
"UPDATE ztc_member SET hidden = true, deleted = true WHERE id = $1 AND network_id = $2" ,
2021-06-02 11:44:00 -07:00
memberId , networkId );
2018-11-30 10:37:27 -08:00
2021-06-02 11:44:00 -07:00
w . commit ();
_pool -> unborrow ( c );
2018-09-05 11:49:07 -07:00
} catch ( std :: exception & e ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s ERROR: Error deleting member: %s \n " , _myAddressStr . c_str (), e . what ());
2018-09-05 11:49:07 -07:00
}
} else {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s ERROR: unknown objtype \n " , _myAddressStr . c_str ());
2018-09-05 11:49:07 -07:00
}
} catch ( std :: exception & e ) {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s ERROR: Error getting objtype: %s \n " , _myAddressStr . c_str (), e . what ());
2018-09-05 11:49:07 -07:00
}
2020-10-05 13:32:47 -07:00
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 100 ));
2018-08-31 14:58:15 -07:00
}
2018-11-30 10:37:27 -08:00
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s commitThread finished \n " , _myAddressStr . c_str ());
2018-08-31 14:58:15 -07:00
}
void PostgreSQL :: onlineNotificationThread ()
2020-05-13 17:23:27 -07:00
{
2020-05-20 16:28:28 -07:00
waitForReady ();
2021-06-02 11:44:00 -07:00
onlineNotification_Postgres ();
2020-05-28 19:22:07 -07:00
}
void PostgreSQL :: onlineNotification_Postgres ()
{
2018-08-31 14:58:15 -07:00
_connected = 1 ;
2020-05-26 19:00:55 -07:00
nlohmann :: json jtmp1 , jtmp2 ;
2020-05-28 19:22:07 -07:00
while ( _run == 1 ) {
2021-09-02 12:45:19 -07:00
auto c = _pool -> borrow ();
2021-06-02 11:44:00 -07:00
try {
2021-09-02 16:22:52 -07:00
fprintf ( stderr , "%s onlineNotification_Postgres \n " , _myAddressStr . c_str ());
2021-06-02 11:44:00 -07:00
std :: unordered_map < std :: pair < uint64_t , uint64_t > , std :: pair < int64_t , InetAddress > , _PairHasher > lastOnline ;
{
std :: lock_guard < std :: mutex > l ( _lastOnline_l );
lastOnline . swap ( _lastOnline );
2018-09-04 14:00:02 -07:00
}
2021-09-02 12:45:19 -07:00
2021-06-02 11:44:00 -07:00
pqxx :: work w ( * c -> c );
2019-08-23 09:23:39 -07:00
2021-06-02 11:44:00 -07:00
// using pqxx::stream_to would be a really nice alternative here, but
// unfortunately it doesn't support upserts.
2021-08-19 12:44:02 -07:00
// fprintf(stderr, "online notification tick\n");
2021-06-02 11:44:00 -07:00
std :: stringstream memberUpdate ;
memberUpdate << "INSERT INTO ztc_member_status (network_id, member_id, address, last_updated) VALUES " ;
bool firstRun = true ;
bool memberAdded = false ;
2021-06-02 14:08:09 -07:00
int updateCount = 0 ;
2021-06-02 11:44:00 -07:00
for ( auto i = lastOnline . begin (); i != lastOnline . end (); ++ i ) {
2021-06-02 14:08:09 -07:00
updateCount += 1 ;
2021-06-02 11:44:00 -07:00
uint64_t nwid_i = i -> first . first ;
char nwidTmp [ 64 ];
char memTmp [ 64 ];
char ipTmp [ 64 ];
OSUtils :: ztsnprintf ( nwidTmp , sizeof ( nwidTmp ), "%.16llx" , nwid_i );
OSUtils :: ztsnprintf ( memTmp , sizeof ( memTmp ), "%.10llx" , i -> first . second );
2018-11-30 10:37:27 -08:00
2021-06-02 11:44:00 -07:00
if ( ! get ( nwid_i , jtmp1 , i -> first . second , jtmp2 )) {
continue ; // skip non existent networks/members
}
2019-04-04 12:40:49 -07:00
2021-06-02 11:44:00 -07:00
std :: string networkId ( nwidTmp );
std :: string memberId ( memTmp );
const char * qvals [ 2 ] = {
networkId . c_str (),
memberId . c_str ()
};
try {
pqxx :: row r = w . exec_params1 ( "SELECT id, network_id FROM ztc_member WHERE network_id = $1 AND id = $2" ,
2021-09-02 12:45:19 -07:00
networkId , memberId );
2021-06-02 11:44:00 -07:00
} catch ( pqxx :: unexpected_rows & e ) {
2021-09-02 12:45:19 -07:00
// fprintf(stderr, "Member count failed: %s\n", e.what());
2021-06-02 11:44:00 -07:00
continue ;
}
2019-04-04 12:40:49 -07:00
int64_t ts = i -> second . first ;
std :: string ipAddr = i -> second . second . toIpString ( ipTmp );
std :: string timestamp = std :: to_string ( ts );
2019-08-23 09:23:39 -07:00
2019-04-04 12:40:49 -07:00
if ( firstRun ) {
firstRun = false ;
} else {
memberUpdate << ", " ;
}
2019-04-04 15:11:01 -07:00
memberUpdate << "('" << networkId << "', '" << memberId << "', " ;
if ( ipAddr . empty ()) {
memberUpdate << "NULL, " ;
} else {
memberUpdate << "'" << ipAddr << "', " ;
}
memberUpdate << "TO_TIMESTAMP(" << timestamp << "::double precision/1000))" ;
2019-04-04 12:40:49 -07:00
memberAdded = true ;
}
2021-06-02 11:44:00 -07:00
memberUpdate << " ON CONFLICT (network_id, member_id) DO UPDATE SET address = EXCLUDED.address, last_updated = EXCLUDED.last_updated;" ;
2019-04-04 12:40:49 -07:00
2021-06-02 11:44:00 -07:00
if ( memberAdded ) {
2021-08-19 17:55:30 -07:00
//fprintf(stderr, "%s\n", memberUpdate.str().c_str());
2021-06-02 11:44:00 -07:00
pqxx :: result res = w . exec0 ( memberUpdate . str ());
2021-06-02 14:08:09 -07:00
w . commit ();
2019-04-04 12:40:49 -07:00
}
2021-09-02 16:32:40 -07:00
fprintf ( stderr , "%s: Updated online status of %d members \n " , _myAddressStr . c_str (), updateCount );
2021-06-02 11:44:00 -07:00
} catch ( std :: exception & e ) {
fprintf ( stderr , "%s: error in onlinenotification thread: %s \n " , _myAddressStr . c_str (), e . what ());
2021-09-02 12:45:19 -07:00
}
_pool -> unborrow ( c );
ConnectionPoolStats stats = _pool -> get_stats ();
2021-09-02 13:48:08 -07:00
fprintf ( stderr , "%s pool stats: in use size: %llu, available size: %llu, total: %llu \n " ,
_myAddressStr . c_str (), stats . borrowed_size , stats . pool_size , ( stats . borrowed_size + stats . pool_size ));
2018-09-04 14:00:02 -07:00
2020-09-10 13:18:25 -07:00
std :: this_thread :: sleep_for ( std :: chrono :: seconds ( 10 ));
2020-05-28 19:22:07 -07:00
}
fprintf ( stderr , "%s: Fell out of run loop in onlineNotificationThread \n " , _myAddressStr . c_str ());
if ( _run == 1 ) {
fprintf ( stderr , "ERROR: %s onlineNotificationThread should still be running! Exiting Controller. \n " , _myAddressStr . c_str ());
exit ( 6 );
}
2018-08-31 14:58:15 -07:00
}
2018-12-03 15:19:15 -08:00
2020-05-13 17:23:27 -07:00
void PostgreSQL :: onlineNotification_Redis ()
{
_connected = 1 ;
char buf [ 11 ] = { 0 };
std :: string controllerId = std :: string ( _myAddress . toString ( buf ));
while ( _run == 1 ) {
std :: unordered_map < std :: pair < uint64_t , uint64_t > , std :: pair < int64_t , InetAddress > , _PairHasher > lastOnline ;
{
std :: lock_guard < std :: mutex > l ( _lastOnline_l );
lastOnline . swap ( _lastOnline );
}
2020-05-20 11:38:04 -07:00
try {
if ( ! lastOnline . empty ()) {
if ( _rc -> clusterMode ) {
2020-05-21 09:49:41 -07:00
auto tx = _cluster -> transaction ( controllerId , true );
2020-05-20 11:38:04 -07:00
_doRedisUpdate ( tx , controllerId , lastOnline );
} else {
auto tx = _redis -> transaction ( true );
_doRedisUpdate ( tx , controllerId , lastOnline );
}
2020-05-18 13:58:29 -07:00
}
2020-05-20 11:38:04 -07:00
} catch ( sw :: redis :: Error & e ) {
2020-05-21 09:49:55 -07:00
#ifdef ZT_TRACE
2020-05-20 11:38:04 -07:00
fprintf ( stderr , "Error in online notification thread (redis): %s \n " , e . what ());
2020-05-21 09:49:55 -07:00
#endif
2020-05-20 11:38:04 -07:00
}
2020-08-21 16:22:28 -07:00
std :: this_thread :: sleep_for ( std :: chrono :: seconds ( 10 ));
2020-05-13 17:23:27 -07:00
}
}
void PostgreSQL :: _doRedisUpdate ( sw :: redis :: Transaction & tx , std :: string & controllerId ,
std :: unordered_map < std :: pair < uint64_t , uint64_t > , std :: pair < int64_t , InetAddress > , _PairHasher > & lastOnline )
{
nlohmann :: json jtmp1 , jtmp2 ;
for ( auto i = lastOnline . begin (); i != lastOnline . end (); ++ i ) {
uint64_t nwid_i = i -> first . first ;
uint64_t memberid_i = i -> first . second ;
char nwidTmp [ 64 ];
char memTmp [ 64 ];
char ipTmp [ 64 ];
OSUtils :: ztsnprintf ( nwidTmp , sizeof ( nwidTmp ), "%.16llx" , nwid_i );
OSUtils :: ztsnprintf ( memTmp , sizeof ( memTmp ), "%.10llx" , memberid_i );
if ( ! get ( nwid_i , jtmp1 , memberid_i , jtmp2 )){
continue ; // skip non existent members/networks
}
std :: string networkId ( nwidTmp );
std :: string memberId ( memTmp );
int64_t ts = i -> second . first ;
std :: string ipAddr = i -> second . second . toIpString ( ipTmp );
std :: string timestamp = std :: to_string ( ts );
std :: unordered_map < std :: string , std :: string > record = {
{ "id" , memberId },
{ "address" , ipAddr },
{ "last_updated" , std :: to_string ( ts )}
};
tx . zadd ( "nodes-online:{" + controllerId + "}" , memberId , ts )
2020-05-27 20:43:20 -07:00
. zadd ( "nodes-online2:{" + controllerId + "}" , networkId + "-" + memberId , ts )
2020-05-13 17:23:27 -07:00
. zadd ( "network-nodes-online:{" + controllerId + "}:" + networkId , memberId , ts )
2020-05-22 10:07:39 -07:00
. zadd ( "active-networks:{" + controllerId + "}" , networkId , ts )
2020-05-13 17:23:27 -07:00
. sadd ( "network-nodes-all:{" + controllerId + "}:" + networkId , memberId )
2020-05-20 16:28:28 -07:00
. hmset ( "member:{" + controllerId + "}:" + networkId + ":" + memberId , record . begin (), record . end ());
2020-05-13 17:23:27 -07:00
}
// expire records from all-nodes and network-nodes member list
uint64_t expireOld = OSUtils :: now () - 300000 ;
tx . zremrangebyscore ( "nodes-online:{" + controllerId + "}" , sw :: redis :: RightBoundedInterval < double > ( expireOld , sw :: redis :: BoundType :: LEFT_OPEN ));
2020-05-27 20:43:20 -07:00
tx . zremrangebyscore ( "nodes-online2:{" + controllerId + "}" , sw :: redis :: RightBoundedInterval < double > ( expireOld , sw :: redis :: BoundType :: LEFT_OPEN ));
2020-05-22 11:07:12 -07:00
tx . zremrangebyscore ( "active-networks:{" + controllerId + "}" , sw :: redis :: RightBoundedInterval < double > ( expireOld , sw :: redis :: BoundType :: LEFT_OPEN ));
2020-05-27 20:41:47 -07:00
{
std :: lock_guard < std :: mutex > l ( _networks_l );
for ( const auto & it : _networks ) {
uint64_t nwid_i = it . first ;
char nwidTmp [ 64 ];
OSUtils :: ztsnprintf ( nwidTmp , sizeof ( nwidTmp ), "%.16llx" , nwid_i );
tx . zremrangebyscore ( "network-nodes-online:{" + controllerId + "}:" + nwidTmp ,
sw :: redis :: RightBoundedInterval < double > ( expireOld , sw :: redis :: BoundType :: LEFT_OPEN ));
}
2020-05-13 17:23:27 -07:00
}
tx . exec ();
}
2019-08-06 07:51:50 -05:00
2018-09-28 10:55:39 -07:00
#endif //ZT_CONTROLLER_USE_LIBPQ