CentralDB.cpp 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778
  1. /*
  2. * Copyright (c)2019 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2026-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #include "CentralDB.hpp"
  14. #ifdef ZT_CONTROLLER_USE_LIBPQ
  15. #include "../../node/Constants.hpp"
  16. #include "../../node/SHA512.hpp"
  17. #include "../../version.h"
  18. #include "BigTableStatusWriter.hpp"
  19. #include "ControllerChangeNotifier.hpp"
  20. #include "ControllerConfig.hpp"
  21. #include "CtlUtil.hpp"
  22. #include "EmbeddedNetworkController.hpp"
  23. #include "PostgresStatusWriter.hpp"
  24. #include "PubSubListener.hpp"
  25. #include "PubSubWriter.hpp"
  26. #include "Redis.hpp"
  27. #include "RedisListener.hpp"
  28. #include "RedisStatusWriter.hpp"
  29. #include "opentelemetry/trace/provider.h"
  30. #include <chrono>
  31. #include <climits>
  32. #include <iomanip>
  33. #include <libpq-fe.h>
  34. #include <optional>
  35. #include <pqxx/pqxx>
  36. #include <rustybits.h>
  37. #include <sstream>
  38. // #define REDIS_TRACE 1
  39. using json = nlohmann::json;
  40. using namespace ZeroTier;
  41. using Attrs = std::vector<std::pair<std::string, std::string> >;
  42. using Item = std::pair<std::string, Attrs>;
  43. using ItemStream = std::vector<Item>;
  44. CentralDB::CentralDB(
  45. const Identity& myId,
  46. const char* connString,
  47. int listenPort,
  48. CentralDB::ListenerMode listenMode,
  49. CentralDB::StatusWriterMode statusMode,
  50. const ControllerConfig* cc)
  51. : DB()
  52. , _listenerMode(listenMode)
  53. , _statusWriterMode(statusMode)
  54. , _cc(cc)
  55. , _pool()
  56. , _myId(myId)
  57. , _myAddress(myId.address())
  58. , _ready(0)
  59. , _connected(1)
  60. , _run(1)
  61. , _waitNoticePrinted(false)
  62. , _listenPort(listenPort)
  63. , _redis(NULL)
  64. , _cluster(NULL)
  65. , _redisMemberStatus(false)
  66. , _smee(NULL)
  67. {
  68. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  69. auto tracer = provider->GetTracer("CentralDB");
  70. auto span = tracer->StartSpan("CentralDB::CentralDB");
  71. auto scope = tracer->WithActiveSpan(span);
  72. rustybits::init_async_runtime();
  73. char myAddress[64];
  74. _myAddressStr = myId.address().toString(myAddress);
  75. _connString = std::string(connString);
  76. auto f = std::make_shared<PostgresConnFactory>(_connString);
  77. _pool =
  78. std::make_shared<ConnectionPool<PostgresConnection> >(15, 5, std::static_pointer_cast<ConnectionFactory>(f));
  79. memset(_ssoPsk, 0, sizeof(_ssoPsk));
  80. char* const ssoPskHex = getenv("ZT_SSO_PSK");
  81. #ifdef ZT_TRACE
  82. fprintf(stderr, "ZT_SSO_PSK: %s\n", ssoPskHex);
  83. #endif
  84. if (ssoPskHex) {
  85. // SECURITY: note that ssoPskHex will always be null-terminated if libc actually
  86. // returns something non-NULL. If the hex encodes something shorter than 48 bytes,
  87. // it will be padded at the end with zeroes. If longer, it'll be truncated.
  88. Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk));
  89. }
  90. const char* redisMemberStatus = getenv("ZT_REDIS_MEMBER_STATUS");
  91. if (redisMemberStatus && (strcmp(redisMemberStatus, "true") == 0)) {
  92. _redisMemberStatus = true;
  93. fprintf(stderr, "Using redis for member status\n");
  94. }
  95. if ((listenMode == LISTENER_MODE_REDIS || statusMode == STATUS_WRITER_MODE_REDIS) && _cc->redisConfig != NULL) {
  96. auto innerspan = tracer->StartSpan("CentralDB::CentralDB::configureRedis");
  97. auto innerscope = tracer->WithActiveSpan(innerspan);
  98. sw::redis::ConnectionOptions opts;
  99. sw::redis::ConnectionPoolOptions poolOpts;
  100. opts.host = _cc->redisConfig->hostname;
  101. opts.port = _cc->redisConfig->port;
  102. opts.password = _cc->redisConfig->password;
  103. opts.db = 0;
  104. opts.keep_alive = true;
  105. opts.connect_timeout = std::chrono::seconds(3);
  106. poolOpts.size = 25;
  107. poolOpts.wait_timeout = std::chrono::seconds(5);
  108. poolOpts.connection_lifetime = std::chrono::minutes(3);
  109. poolOpts.connection_idle_time = std::chrono::minutes(1);
  110. if (_cc->redisConfig->clusterMode) {
  111. innerspan->SetAttribute("cluster_mode", "true");
  112. fprintf(stderr, "Using Redis in Cluster Mode\n");
  113. _cluster = std::make_shared<sw::redis::RedisCluster>(opts, poolOpts);
  114. }
  115. else {
  116. innerspan->SetAttribute("cluster_mode", "false");
  117. fprintf(stderr, "Using Redis in Standalone Mode\n");
  118. _redis = std::make_shared<sw::redis::Redis>(opts, poolOpts);
  119. }
  120. }
  121. _readyLock.lock();
  122. fprintf(
  123. stderr, "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S,
  124. ::_timestr(), (unsigned long long)_myAddress.toInt());
  125. _waitNoticePrinted = true;
  126. initializeNetworks();
  127. initializeMembers();
  128. _heartbeatThread = std::thread(&CentralDB::heartbeat, this);
  129. switch (listenMode) {
  130. case LISTENER_MODE_REDIS:
  131. fprintf(stderr, "Using Redis for change listeners\n");
  132. if (_cc->redisConfig != NULL) {
  133. if (_cc->redisConfig->clusterMode) {
  134. _membersDbWatcher = std::make_shared<RedisMemberListener>(_myAddressStr, _cluster, this);
  135. _networksDbWatcher = std::make_shared<RedisNetworkListener>(_myAddressStr, _cluster, this);
  136. }
  137. else {
  138. _membersDbWatcher = std::make_shared<RedisMemberListener>(_myAddressStr, _redis, this);
  139. _networksDbWatcher = std::make_shared<RedisNetworkListener>(_myAddressStr, _redis, this);
  140. }
  141. }
  142. else {
  143. throw std::runtime_error("CentralDB: Redis listener mode selected but no Redis configuration provided");
  144. }
  145. case LISTENER_MODE_PUBSUB:
  146. fprintf(stderr, "Using PubSub for change listeners\n");
  147. if (cc->pubSubConfig != NULL) {
  148. _membersDbWatcher =
  149. std::make_shared<PubSubMemberListener>(_myAddressStr, cc->pubSubConfig->project_id, this);
  150. _networksDbWatcher =
  151. std::make_shared<PubSubNetworkListener>(_myAddressStr, cc->pubSubConfig->project_id, this);
  152. _changeNotifier = std::make_shared<PubSubChangeNotifier>(_myAddressStr, cc->pubSubConfig->project_id);
  153. }
  154. else {
  155. throw std::runtime_error(
  156. "CentralDB: PubSub listener mode selected but no PubSub configuration provided");
  157. }
  158. break;
  159. case LISTENER_MODE_PGSQL:
  160. default:
  161. fprintf(stderr, "Using PostgreSQL for change listeners\n");
  162. _membersDbWatcher = std::make_shared<PostgresMemberListener>(this, _pool, "member_" + _myAddressStr, 5);
  163. _networksDbWatcher = std::make_shared<PostgresNetworkListener>(this, _pool, "network_" + _myAddressStr, 5);
  164. break;
  165. }
  166. std::shared_ptr<PubSubWriter> pubsubWriter;
  167. switch (statusMode) {
  168. case STATUS_WRITER_MODE_REDIS:
  169. fprintf(stderr, "Using Redis for status writer\n");
  170. if (_cc->redisConfig != NULL) {
  171. if (_cc->redisConfig->clusterMode) {
  172. _statusWriter = std::make_shared<RedisStatusWriter>(_cluster, _myAddressStr);
  173. }
  174. else {
  175. _statusWriter = std::make_shared<RedisStatusWriter>(_redis, _myAddressStr);
  176. }
  177. }
  178. else {
  179. throw std::runtime_error("CentralDB: Redis status mode selected but no Redis configuration provided");
  180. }
  181. break;
  182. case STATUS_WRITER_MODE_BIGTABLE:
  183. fprintf(stderr, "Using BigTable for status writer\n");
  184. if (cc->bigTableConfig == NULL) {
  185. throw std::runtime_error(
  186. "CentralDB: BigTable status mode selected but no BigTable configuration provided");
  187. }
  188. if (cc->pubSubConfig == NULL) {
  189. throw std::runtime_error(
  190. "CentralDB: BigTable status mode selected but no PubSub configuration provided");
  191. }
  192. _statusWriter = std::make_shared<BigTableStatusWriter>(
  193. cc->bigTableConfig->project_id, cc->bigTableConfig->instance_id, cc->bigTableConfig->table_id);
  194. break;
  195. case STATUS_WRITER_MODE_PGSQL:
  196. default:
  197. fprintf(stderr, "Using PostgreSQL for status writer\n");
  198. _statusWriter = std::make_shared<PostgresStatusWriter>(_pool);
  199. break;
  200. }
  201. // start background threads
  202. for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) {
  203. _commitThread[i] = std::thread(&CentralDB::commitThread, this);
  204. }
  205. _onlineNotificationThread = std::thread(&CentralDB::onlineNotificationThread, this);
  206. configureSmee();
  207. }
  208. CentralDB::~CentralDB()
  209. {
  210. if (_smee != NULL) {
  211. rustybits::smee_client_delete(_smee);
  212. _smee = NULL;
  213. }
  214. rustybits::shutdown_async_runtime();
  215. _run = 0;
  216. std::this_thread::sleep_for(std::chrono::milliseconds(100));
  217. _heartbeatThread.join();
  218. _commitQueue.stop();
  219. for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) {
  220. _commitThread[i].join();
  221. }
  222. _onlineNotificationThread.join();
  223. }
  224. void CentralDB::configureSmee()
  225. {
  226. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  227. auto tracer = provider->GetTracer("CentralDB");
  228. auto span = tracer->StartSpan("CentralDB::configureSmee");
  229. auto scope = tracer->WithActiveSpan(span);
  230. const char* TEMPORAL_SCHEME = "ZT_TEMPORAL_SCHEME";
  231. const char* TEMPORAL_HOST = "ZT_TEMPORAL_HOST";
  232. const char* TEMPORAL_PORT = "ZT_TEMPORAL_PORT";
  233. const char* TEMPORAL_NAMESPACE = "ZT_TEMPORAL_NAMESPACE";
  234. const char* SMEE_TASK_QUEUE = "ZT_SMEE_TASK_QUEUE";
  235. const char* scheme = getenv(TEMPORAL_SCHEME);
  236. if (scheme == NULL) {
  237. scheme = "http";
  238. }
  239. const char* host = getenv(TEMPORAL_HOST);
  240. const char* port = getenv(TEMPORAL_PORT);
  241. const char* ns = getenv(TEMPORAL_NAMESPACE);
  242. const char* task_queue = getenv(SMEE_TASK_QUEUE);
  243. if (scheme != NULL && host != NULL && port != NULL && ns != NULL && task_queue != NULL) {
  244. fprintf(stderr, "creating smee client\n");
  245. std::string hostPort =
  246. std::string(scheme) + std::string("://") + std::string(host) + std::string(":") + std::string(port);
  247. this->_smee = rustybits::smee_client_new(hostPort.c_str(), ns, task_queue);
  248. }
  249. else {
  250. fprintf(stderr, "Smee client not configured\n");
  251. }
  252. }
  253. bool CentralDB::waitForReady()
  254. {
  255. while (_ready < 2) {
  256. _readyLock.lock();
  257. _readyLock.unlock();
  258. }
  259. return true;
  260. }
  261. bool CentralDB::isReady()
  262. {
  263. return ((_ready == 2) && (_connected));
  264. }
  265. bool CentralDB::save(nlohmann::json& record, bool notifyListeners)
  266. {
  267. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  268. auto tracer = provider->GetTracer("CentralDB");
  269. auto span = tracer->StartSpan("CentralDB::save");
  270. auto scope = tracer->WithActiveSpan(span);
  271. bool modified = false;
  272. try {
  273. if (! record.is_object()) {
  274. fprintf(stderr, "record is not an object?!?\n");
  275. return false;
  276. }
  277. const std::string objtype = record["objtype"];
  278. if (objtype == "network") {
  279. // fprintf(stderr, "network save\n");
  280. const uint64_t nwid = OSUtils::jsonIntHex(record["id"], 0ULL);
  281. if (nwid) {
  282. nlohmann::json old;
  283. get(nwid, old);
  284. if ((! old.is_object()) || (! _compareRecords(old, record))) {
  285. record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL;
  286. _commitQueue.post(std::pair<nlohmann::json, bool>(record, notifyListeners));
  287. modified = true;
  288. }
  289. }
  290. }
  291. else if (objtype == "member") {
  292. std::string networkId = record["nwid"];
  293. std::string memberId = record["id"];
  294. const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"], 0ULL);
  295. const uint64_t id = OSUtils::jsonIntHex(record["id"], 0ULL);
  296. // fprintf(stderr, "member save %s-%s\n", networkId.c_str(), memberId.c_str());
  297. if ((id) && (nwid)) {
  298. nlohmann::json network, old;
  299. get(nwid, network, id, old);
  300. if ((! old.is_object()) || (! _compareRecords(old, record))) {
  301. // fprintf(stderr, "commit queue post\n");
  302. record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL;
  303. _commitQueue.post(std::pair<nlohmann::json, bool>(record, notifyListeners));
  304. modified = true;
  305. }
  306. else {
  307. // fprintf(stderr, "no change\n");
  308. }
  309. }
  310. }
  311. else {
  312. fprintf(stderr, "uhh waaat\n");
  313. }
  314. }
  315. catch (std::exception& e) {
  316. fprintf(stderr, "Error on PostgreSQL::save: %s\n", e.what());
  317. }
  318. catch (...) {
  319. fprintf(stderr, "Unknown error on PostgreSQL::save\n");
  320. }
  321. return modified;
  322. }
  323. void CentralDB::eraseNetwork(const uint64_t networkId)
  324. {
  325. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  326. auto tracer = provider->GetTracer("CentralDB");
  327. auto span = tracer->StartSpan("CentralDB::eraseNetwork");
  328. auto scope = tracer->WithActiveSpan(span);
  329. char networkIdStr[17];
  330. span->SetAttribute("network_id", Utils::hex(networkId, networkIdStr));
  331. fprintf(stderr, "PostgreSQL::eraseNetwork\n");
  332. char tmp2[24];
  333. waitForReady();
  334. Utils::hex(networkId, tmp2);
  335. std::pair<nlohmann::json, bool> tmp;
  336. tmp.first["id"] = tmp2;
  337. tmp.first["objtype"] = "_delete_network";
  338. tmp.second = true;
  339. _commitQueue.post(tmp);
  340. nlohmann::json nullJson;
  341. _networkChanged(tmp.first, nullJson, true);
  342. }
  343. void CentralDB::eraseMember(const uint64_t networkId, const uint64_t memberId)
  344. {
  345. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  346. auto tracer = provider->GetTracer("CentralDB");
  347. auto span = tracer->StartSpan("CentralDB::eraseMember");
  348. auto scope = tracer->WithActiveSpan(span);
  349. char networkIdStr[17];
  350. char memberIdStr[11];
  351. span->SetAttribute("network_id", Utils::hex(networkId, networkIdStr));
  352. span->SetAttribute("member_id", Utils::hex10(memberId, memberIdStr));
  353. fprintf(stderr, "PostgreSQL::eraseMember\n");
  354. char tmp2[24];
  355. waitForReady();
  356. std::pair<nlohmann::json, bool> tmp, nw;
  357. Utils::hex(networkId, tmp2);
  358. tmp.first["nwid"] = tmp2;
  359. Utils::hex(memberId, tmp2);
  360. tmp.first["id"] = tmp2;
  361. tmp.first["objtype"] = "_delete_member";
  362. tmp.second = true;
  363. _commitQueue.post(tmp);
  364. nlohmann::json nullJson;
  365. _memberChanged(tmp.first, nullJson, true);
  366. }
  367. void CentralDB::nodeIsOnline(
  368. const uint64_t networkId,
  369. const uint64_t memberId,
  370. const InetAddress& physicalAddress,
  371. const char* osArch)
  372. {
  373. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  374. auto tracer = provider->GetTracer("CentralDB");
  375. auto span = tracer->StartSpan("CentralDB::nodeIsOnline");
  376. auto scope = tracer->WithActiveSpan(span);
  377. char networkIdStr[17];
  378. char memberIdStr[11];
  379. char ipStr[INET6_ADDRSTRLEN];
  380. span->SetAttribute("network_id", Utils::hex(networkId, networkIdStr));
  381. span->SetAttribute("member_id", Utils::hex10(memberId, memberIdStr));
  382. span->SetAttribute("physical_address", physicalAddress.toString(ipStr));
  383. span->SetAttribute("os_arch", osArch);
  384. std::lock_guard<std::mutex> l(_lastOnline_l);
  385. NodeOnlineRecord& i = _lastOnline[std::pair<uint64_t, uint64_t>(networkId, memberId)];
  386. i.lastSeen = OSUtils::now();
  387. if (physicalAddress) {
  388. i.physicalAddress = physicalAddress;
  389. }
  390. i.osArch = std::string(osArch);
  391. }
  392. void CentralDB::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress)
  393. {
  394. this->nodeIsOnline(networkId, memberId, physicalAddress, "unknown/unknown");
  395. }
  396. AuthInfo CentralDB::getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL)
  397. {
  398. if (_cc->ssoEnabled) {
  399. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  400. auto tracer = provider->GetTracer("CentralDB");
  401. auto span = tracer->StartSpan("CentralDB::getSSOAuthInfo");
  402. auto scope = tracer->WithActiveSpan(span);
  403. Metrics::db_get_sso_info++;
  404. // NONCE is just a random character string. no semantic meaning
  405. // state = HMAC SHA384 of Nonce based on shared sso key
  406. //
  407. // need nonce timeout in database? make sure it's used within X time
  408. // X is 5 minutes for now. Make configurable later?
  409. //
  410. // how do we tell when a nonce is used? if auth_expiration_time is set
  411. std::string networkId = member["nwid"];
  412. std::string memberId = member["id"];
  413. char authenticationURL[4096] = { 0 };
  414. AuthInfo info;
  415. info.enabled = true;
  416. // if (memberId == "a10dccea52" && networkId == "8056c2e21c24673d") {
  417. // fprintf(stderr, "invalid authinfo for grant's machine\n");
  418. // info.version=1;
  419. // return info;
  420. // }
  421. // fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str());
  422. std::shared_ptr<PostgresConnection> c;
  423. try {
  424. c = _pool->borrow();
  425. pqxx::work w(*c->c);
  426. char nonceBytes[16] = { 0 };
  427. std::string nonce = "";
  428. // check if the member exists first.
  429. pqxx::row count =
  430. w.exec(
  431. "SELECT count(id) FROM ztc_member WHERE id = $1 AND network_id = $2 AND deleted = false",
  432. pqxx::params { memberId, networkId })
  433. .one_row();
  434. if (count[0].as<int>() == 1) {
  435. // get active nonce, if exists.
  436. pqxx::result r = w.exec(
  437. "SELECT nonce FROM ztc_sso_expiry "
  438. "WHERE network_id = $1 AND member_id = $2 "
  439. "AND ((NOW() AT TIME ZONE 'UTC') <= authentication_expiry_time) AND ((NOW() AT TIME ZONE 'UTC') <= "
  440. "nonce_expiration)",
  441. pqxx::params { networkId, memberId });
  442. if (r.size() == 0) {
  443. // no active nonce.
  444. // find an unused nonce, if one exists.
  445. pqxx::result r = w.exec(
  446. "SELECT nonce FROM ztc_sso_expiry "
  447. "WHERE network_id = $1 AND member_id = $2 "
  448. "AND authentication_expiry_time IS NULL AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)",
  449. pqxx::params { networkId, memberId });
  450. if (r.size() == 1) {
  451. // we have an existing nonce. Use it
  452. nonce = r.at(0)[0].as<std::string>();
  453. Utils::unhex(nonce.c_str(), nonceBytes, sizeof(nonceBytes));
  454. }
  455. else if (r.empty()) {
  456. // create a nonce
  457. Utils::getSecureRandom(nonceBytes, 16);
  458. char nonceBuf[64] = { 0 };
  459. Utils::hex(nonceBytes, sizeof(nonceBytes), nonceBuf);
  460. nonce = std::string(nonceBuf);
  461. pqxx::result ir = w.exec(
  462. "INSERT INTO ztc_sso_expiry "
  463. "(nonce, nonce_expiration, network_id, member_id) VALUES "
  464. "($1, TO_TIMESTAMP($2::double precision/1000), $3, $4)",
  465. pqxx::params { nonce, OSUtils::now() + 300000, networkId, memberId });
  466. w.commit();
  467. }
  468. else {
  469. // > 1 ?!? Thats an error!
  470. fprintf(stderr, "> 1 unused nonce!\n");
  471. exit(6);
  472. }
  473. }
  474. else if (r.size() == 1) {
  475. nonce = r.at(0)[0].as<std::string>();
  476. Utils::unhex(nonce.c_str(), nonceBytes, sizeof(nonceBytes));
  477. }
  478. else {
  479. // more than 1 nonce in use? Uhhh...
  480. fprintf(stderr, "> 1 nonce in use for network member?!?\n");
  481. exit(7);
  482. }
  483. r = w.exec(
  484. "SELECT oc.client_id, oc.authorization_endpoint, oc.issuer, oc.provider, oc.sso_impl_version "
  485. "FROM ztc_network AS n "
  486. "INNER JOIN ztc_org o "
  487. " ON o.owner_id = n.owner_id "
  488. "LEFT OUTER JOIN ztc_network_oidc_config noc "
  489. " ON noc.network_id = n.id "
  490. "LEFT OUTER JOIN ztc_oidc_config oc "
  491. " ON noc.client_id = oc.client_id AND oc.org_id = o.org_id "
  492. "WHERE n.id = $1 AND n.sso_enabled = true",
  493. pqxx::params { networkId });
  494. std::string client_id = "";
  495. std::string authorization_endpoint = "";
  496. std::string issuer = "";
  497. std::string provider = "";
  498. uint64_t sso_version = 0;
  499. if (r.size() == 1) {
  500. client_id = r.at(0)[0].as<std::optional<std::string> >().value_or("");
  501. authorization_endpoint = r.at(0)[1].as<std::optional<std::string> >().value_or("");
  502. issuer = r.at(0)[2].as<std::optional<std::string> >().value_or("");
  503. provider = r.at(0)[3].as<std::optional<std::string> >().value_or("");
  504. sso_version = r.at(0)[4].as<std::optional<uint64_t> >().value_or(1);
  505. }
  506. else if (r.size() > 1) {
  507. fprintf(
  508. stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n",
  509. networkId.c_str());
  510. }
  511. else {
  512. fprintf(stderr, "No client or auth endpoint?!?\n");
  513. }
  514. info.version = sso_version;
  515. // no catch all else because we don't actually care if no records exist here. just continue as normal.
  516. if ((! client_id.empty()) && (! authorization_endpoint.empty())) {
  517. uint8_t state[48];
  518. HMACSHA384(_ssoPsk, nonceBytes, sizeof(nonceBytes), state);
  519. char state_hex[256];
  520. Utils::hex(state, 48, state_hex);
  521. if (info.version == 0) {
  522. char url[2048] = { 0 };
  523. OSUtils::ztsnprintf(
  524. url, sizeof(authenticationURL),
  525. "%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri="
  526. "%s&nonce=%s&state=%s&client_id=%s",
  527. authorization_endpoint.c_str(), url_encode(redirectURL).c_str(), nonce.c_str(), state_hex,
  528. client_id.c_str());
  529. info.authenticationURL = std::string(url);
  530. }
  531. else if (info.version == 1) {
  532. info.ssoClientID = client_id;
  533. info.issuerURL = issuer;
  534. info.ssoProvider = provider;
  535. info.ssoNonce = nonce;
  536. info.ssoState = std::string(state_hex) + "_" + networkId;
  537. info.centralAuthURL = redirectURL;
  538. #ifdef ZT_DEBUG
  539. fprintf(
  540. stderr,
  541. "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s\nprovider: "
  542. "%s\n",
  543. info.ssoClientID.c_str(), info.issuerURL.c_str(), info.ssoNonce.c_str(),
  544. info.ssoState.c_str(), info.centralAuthURL.c_str(), provider.c_str());
  545. #endif
  546. }
  547. }
  548. else {
  549. fprintf(
  550. stderr, "client_id: %s\nauthorization_endpoint: %s\n", client_id.c_str(),
  551. authorization_endpoint.c_str());
  552. }
  553. }
  554. _pool->unborrow(c);
  555. }
  556. catch (std::exception& e) {
  557. span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  558. fprintf(stderr, "ERROR: Error updating member on load for network %s: %s\n", networkId.c_str(), e.what());
  559. }
  560. return info; // std::string(authenticationURL);
  561. }
  562. return AuthInfo();
  563. }
  564. void CentralDB::initializeNetworks()
  565. {
  566. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  567. auto tracer = provider->GetTracer("CentralDB");
  568. auto span = tracer->StartSpan("CentralDB::initializeNetworks");
  569. auto scope = tracer->WithActiveSpan(span);
  570. fprintf(stderr, "Initializing networks...\n");
  571. try {
  572. char qbuf[2048];
  573. sprintf(
  574. qbuf,
  575. "SELECT id, name, configuration , (EXTRACT(EPOCH FROM creation_time AT TIME ZONE 'UTC')*1000)::bigint, "
  576. "(EXTRACT(EPOCH FROM last_modified AT TIME ZONE 'UTC')*1000)::bigint, revision, frontend "
  577. "FROM networks_ctl WHERE controller_id = '%s'",
  578. _myAddressStr.c_str());
  579. auto c = _pool->borrow();
  580. pqxx::work w(*c->c);
  581. fprintf(stderr, "Load networks from psql...\n");
  582. auto stream = pqxx::stream_from::query(w, qbuf);
  583. std::tuple<
  584. std::string // network ID
  585. ,
  586. std::optional<std::string> // name
  587. ,
  588. std::string // configuration
  589. ,
  590. std::optional<uint64_t> // creation_time
  591. ,
  592. std::optional<uint64_t> // last_modified
  593. ,
  594. std::optional<uint64_t> // revision
  595. ,
  596. std::string // frontend
  597. >
  598. row;
  599. uint64_t count = 0;
  600. uint64_t total = 0;
  601. while (stream >> row) {
  602. auto start = std::chrono::high_resolution_clock::now();
  603. json empty;
  604. json config;
  605. initNetwork(config);
  606. std::string nwid = std::get<0>(row);
  607. std::string name = std::get<1>(row).value_or("");
  608. json cfgtmp = json::parse(std::get<2>(row));
  609. std::optional<uint64_t> created_at = std::get<3>(row);
  610. std::optional<uint64_t> last_modified = std::get<4>(row);
  611. std::optional<uint64_t> revision = std::get<5>(row);
  612. config["id"] = nwid;
  613. config["name"] = name;
  614. config["creationTime"] = created_at.value_or(0);
  615. config["lastModified"] = last_modified.value_or(0);
  616. config["revision"] = revision.value_or(0);
  617. config["capabilities"] = cfgtmp["capabilities"].is_array() ? cfgtmp["capabilities"] : json::array();
  618. config["enableBroadcast"] =
  619. cfgtmp["enableBroadcast"].is_boolean() ? cfgtmp["enableBroadcast"].get<bool>() : false;
  620. config["mtu"] = cfgtmp["mtu"].is_number() ? cfgtmp["mtu"].get<int32_t>() : 2800;
  621. config["multicastLimit"] =
  622. cfgtmp["multicastLimit"].is_number() ? cfgtmp["multicastLimit"].get<int32_t>() : 64;
  623. config["private"] = cfgtmp["private"].is_boolean() ? cfgtmp["private"].get<bool>() : true;
  624. config["remoteTraceLevel"] =
  625. cfgtmp["remoteTraceLevel"].is_number() ? cfgtmp["remoteTraceLevel"].get<int32_t>() : 0;
  626. config["remoteTraceTarget"] =
  627. cfgtmp["remoteTraceTarget"].is_string() ? cfgtmp["remoteTraceTarget"].get<std::string>() : "";
  628. config["revision"] = revision.value_or(0);
  629. config["rules"] = cfgtmp["rules"].is_array() ? cfgtmp["rules"] : json::array();
  630. config["tags"] = cfgtmp["tags"].is_array() ? cfgtmp["tags"] : json::array();
  631. if (cfgtmp["v4AssignMode"].is_object()) {
  632. config["v4AssignMode"] = cfgtmp["v4AssignMode"];
  633. }
  634. else {
  635. config["v4AssignMode"] = json::object();
  636. config["v4AssignMode"]["zt"] = true;
  637. }
  638. if (cfgtmp["v6AssignMode"].is_object()) {
  639. config["v6AssignMode"] = cfgtmp["v6AssignMode"];
  640. }
  641. else {
  642. config["v6AssignMode"] = json::object();
  643. config["v6AssignMode"]["zt"] = true;
  644. config["v6AssignMode"]["6plane"] = true;
  645. config["v6AssignMode"]["rfc4193"] = false;
  646. }
  647. config["ssoEnabled"] = cfgtmp["ssoEnabled"].is_boolean() ? cfgtmp["ssoEnabled"].get<bool>() : false;
  648. config["objtype"] = "network";
  649. config["routes"] = cfgtmp["routes"].is_array() ? cfgtmp["routes"] : json::array();
  650. config["clientId"] = cfgtmp["clientId"].is_string() ? cfgtmp["clientId"].get<std::string>() : "";
  651. config["authorizationEndpoint"] = cfgtmp["authorizationEndpoint"].is_string()
  652. ? cfgtmp["authorizationEndpoint"].get<std::string>()
  653. : nullptr;
  654. config["provider"] = cfgtmp["ssoProvider"].is_string() ? cfgtmp["ssoProvider"].get<std::string>() : "";
  655. if (! cfgtmp["dns"].is_object()) {
  656. cfgtmp["dns"] = json::object();
  657. cfgtmp["dns"]["domain"] = "";
  658. cfgtmp["dns"]["servers"] = json::array();
  659. }
  660. else {
  661. config["dns"] = cfgtmp["dns"];
  662. }
  663. config["ipAssignmentPools"] =
  664. cfgtmp["ipAssignmentPools"].is_array() ? cfgtmp["ipAssignmentPools"] : json::array();
  665. config["frontend"] = std::get<6>(row);
  666. Metrics::network_count++;
  667. _networkChanged(empty, config, false);
  668. auto end = std::chrono::high_resolution_clock::now();
  669. auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
  670. ;
  671. total += dur.count();
  672. ++count;
  673. if (count > 0 && count % 10000 == 0) {
  674. fprintf(stderr, "Averaging %lu us per network\n", (total / count));
  675. }
  676. }
  677. w.commit();
  678. _pool->unborrow(c);
  679. fprintf(stderr, "done.\n");
  680. if (++this->_ready == 2) {
  681. if (_waitNoticePrinted) {
  682. fprintf(
  683. stderr, "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S, _timestr(),
  684. (unsigned long long)_myAddress.toInt());
  685. }
  686. _readyLock.unlock();
  687. }
  688. fprintf(stderr, "network init done\n");
  689. }
  690. catch (std::exception& e) {
  691. fprintf(stderr, "ERROR: Error initializing networks: %s\n", e.what());
  692. span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  693. std::this_thread::sleep_for(std::chrono::milliseconds(5000));
  694. exit(-1);
  695. }
  696. }
  697. void CentralDB::initializeMembers()
  698. {
  699. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  700. auto tracer = provider->GetTracer("CentralDB");
  701. auto span = tracer->StartSpan("CentralDB::initializeMembers");
  702. auto scope = tracer->WithActiveSpan(span);
  703. std::string memberId;
  704. std::string networkId;
  705. try {
  706. std::unordered_map<std::string, std::string> networkMembers;
  707. fprintf(stderr, "Initializing Members...\n");
  708. std::string setKeyBase = "network-nodes-all:{" + _myAddressStr + "}:";
  709. if (_redisMemberStatus) {
  710. fprintf(stderr, "Initialize Redis for members...\n");
  711. std::unique_lock<std::shared_mutex> l(_networks_l);
  712. std::unordered_set<std::string> deletes;
  713. for (auto it : _networks) {
  714. uint64_t nwid_i = it.first;
  715. char nwidTmp[64] = { 0 };
  716. OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i);
  717. std::string nwid(nwidTmp);
  718. std::string key = setKeyBase + nwid;
  719. deletes.insert(key);
  720. }
  721. if (! deletes.empty()) {
  722. try {
  723. if (_cc->redisConfig->clusterMode) {
  724. auto tx = _cluster->transaction(_myAddressStr, true, false);
  725. for (std::string k : deletes) {
  726. tx.del(k);
  727. }
  728. tx.exec();
  729. }
  730. else {
  731. auto tx = _redis->transaction(true, false);
  732. for (std::string k : deletes) {
  733. tx.del(k);
  734. }
  735. tx.exec();
  736. }
  737. }
  738. catch (sw::redis::Error& e) {
  739. // ignore
  740. }
  741. }
  742. }
  743. char qbuf[2048];
  744. sprintf(
  745. qbuf,
  746. "SELECT nm.device_id, nm.network_id, nm.authorized, nm.active_bridge, nm.ip_assignments, "
  747. "nm.no_auto_assign_ips, "
  748. "nm.sso_exempt, (EXTRACT(EPOCH FROM nm.authentication_expiry_time AT TIME ZONE 'UTC')*1000)::bigint, "
  749. "(EXTRACT(EPOCH FROM nm.creation_time AT TIME ZONE 'UTC')*1000)::bigint, nm.identity, "
  750. "(EXTRACT(EPOCH FROM nm.last_authorized_time AT TIME ZONE 'UTC')*1000)::bigint, "
  751. "(EXTRACT(EPOCH FROM nm.last_deauthorized_time AT TIME ZONE 'UTC')*1000)::bigint, "
  752. "nm.remote_trace_level, nm.remote_trace_target, nm.revision, nm.capabilities, nm.tags, "
  753. "nm.frontend "
  754. "FROM network_memberships_ctl nm "
  755. "INNER JOIN networks_ctl n "
  756. " ON nm.network_id = n.id "
  757. "WHERE n.controller_id = '%s'",
  758. _myAddressStr.c_str());
  759. auto c = _pool->borrow();
  760. pqxx::work w(*c->c);
  761. fprintf(stderr, "Load members from psql...\n");
  762. auto stream = pqxx::stream_from::query(w, qbuf);
  763. std::tuple<
  764. std::string // device ID
  765. ,
  766. std::string // network ID
  767. ,
  768. bool // authorized
  769. ,
  770. std::optional<bool> // active_bridge
  771. ,
  772. std::optional<std::string> // ip_assignments
  773. ,
  774. std::optional<bool> // no_auto_assign_ips
  775. ,
  776. std::optional<bool> // sso_exempt
  777. ,
  778. std::optional<uint64_t> // authentication_expiry_time
  779. ,
  780. std::optional<uint64_t> // creation_time
  781. ,
  782. std::optional<std::string> // identity
  783. ,
  784. std::optional<uint64_t> // last_authorized_time
  785. ,
  786. std::optional<uint64_t> // last_deauthorized_time
  787. ,
  788. std::optional<int32_t> // remote_trace_level
  789. ,
  790. std::optional<std::string> // remote_trace_target
  791. ,
  792. std::optional<uint64_t> // revision
  793. ,
  794. std::optional<std::string> // capabilities
  795. ,
  796. std::optional<std::string> // tags
  797. ,
  798. std::string // frontend
  799. >
  800. row;
  801. auto tmp = std::chrono::high_resolution_clock::now();
  802. uint64_t count = 0;
  803. uint64_t total = 0;
  804. while (stream >> row) {
  805. auto start = std::chrono::high_resolution_clock::now();
  806. json empty;
  807. json config;
  808. initMember(config);
  809. memberId = std::get<0>(row);
  810. networkId = std::get<1>(row);
  811. bool authorized = std::get<2>(row);
  812. std::optional<bool> active_bridge = std::get<3>(row);
  813. std::string ip_assignments = std::get<4>(row).value_or("");
  814. std::optional<bool> no_auto_assign_ips = std::get<5>(row);
  815. std::optional<bool> sso_exempt = std::get<6>(row);
  816. std::optional<uint64_t> authentication_expiry_time = std::get<7>(row);
  817. std::optional<uint64_t> creation_time = std::get<8>(row);
  818. std::optional<std::string> identity = std::get<9>(row);
  819. std::optional<uint64_t> last_authorized_time = std::get<10>(row);
  820. std::optional<uint64_t> last_deauthorized_time = std::get<11>(row);
  821. std::optional<int32_t> remote_trace_level = std::get<12>(row);
  822. std::optional<std::string> remote_trace_target = std::get<13>(row);
  823. std::optional<uint64_t> revision = std::get<14>(row);
  824. std::optional<std::string> capabilities = std::get<15>(row);
  825. std::optional<std::string> tags = std::get<16>(row);
  826. networkMembers.insert(std::pair<std::string, std::string>(setKeyBase + networkId, memberId));
  827. config["objtype"] = "member";
  828. config["id"] = memberId;
  829. config["address"] = identity.value_or("");
  830. config["nwid"] = networkId;
  831. config["authorized"] = authorized;
  832. config["activeBridge"] = active_bridge.value_or(false);
  833. config["ipAssignments"] = json::array();
  834. if (ip_assignments != "{}") {
  835. std::string tmp = ip_assignments.substr(1, ip_assignments.length() - 2);
  836. std::vector<std::string> addrs = split(tmp, ',');
  837. for (auto it = addrs.begin(); it != addrs.end(); ++it) {
  838. config["ipAssignments"].push_back(*it);
  839. }
  840. }
  841. config["capabilities"] = json::parse(capabilities.value_or("[]"));
  842. config["creationTime"] = creation_time.value_or(0);
  843. config["lastAuthorizedTime"] = last_authorized_time.value_or(0);
  844. config["lastDeauthorizedTime"] = last_deauthorized_time.value_or(0);
  845. config["noAutoAssignIPs"] = no_auto_assign_ips.value_or(false);
  846. config["remoteTraceLevel"] = remote_trace_level.value_or(0);
  847. config["remoteTraceTarget"] = remote_trace_target.value_or(nullptr);
  848. config["revision"] = revision.value_or(0);
  849. config["ssoExempt"] = sso_exempt.value_or(false);
  850. config["authenticationExpiryTime"] = authentication_expiry_time.value_or(0);
  851. config["tags"] = json::parse(tags.value_or("[]"));
  852. config["frontend"] = std::get<17>(row);
  853. Metrics::member_count++;
  854. _memberChanged(empty, config, false);
  855. memberId = "";
  856. networkId = "";
  857. auto end = std::chrono::high_resolution_clock::now();
  858. auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
  859. total += dur.count();
  860. ++count;
  861. if (count > 0 && count % 10000 == 0) {
  862. fprintf(stderr, "Averaging %llu us per member\n", (total / count));
  863. }
  864. }
  865. if (count > 0) {
  866. fprintf(stderr, "Took %llu us per member to load\n", (total / count));
  867. }
  868. stream.complete();
  869. w.commit();
  870. _pool->unborrow(c);
  871. fprintf(stderr, "done.\n");
  872. if (_listenerMode == LISTENER_MODE_REDIS)
  873. if (! networkMembers.empty()) {
  874. if (_redisMemberStatus) {
  875. fprintf(stderr, "Load member data into redis...\n");
  876. if (_cc->redisConfig->clusterMode) {
  877. auto tx = _cluster->transaction(_myAddressStr, true, false);
  878. uint64_t count = 0;
  879. for (auto it : networkMembers) {
  880. tx.sadd(it.first, it.second);
  881. if (++count % 30000 == 0) {
  882. tx.exec();
  883. tx = _cluster->transaction(_myAddressStr, true, false);
  884. }
  885. }
  886. tx.exec();
  887. }
  888. else {
  889. auto tx = _redis->transaction(true, false);
  890. uint64_t count = 0;
  891. for (auto it : networkMembers) {
  892. tx.sadd(it.first, it.second);
  893. if (++count % 30000 == 0) {
  894. tx.exec();
  895. tx = _redis->transaction(true, false);
  896. }
  897. }
  898. tx.exec();
  899. }
  900. fprintf(stderr, "done.\n");
  901. }
  902. }
  903. fprintf(stderr, "Done loading members...\n");
  904. if (++this->_ready == 2) {
  905. if (_waitNoticePrinted) {
  906. fprintf(
  907. stderr, "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S, _timestr(),
  908. (unsigned long long)_myAddress.toInt());
  909. }
  910. _readyLock.unlock();
  911. }
  912. }
  913. catch (sw::redis::Error& e) {
  914. span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  915. fprintf(stderr, "ERROR: Error initializing members (redis): %s\n", e.what());
  916. exit(-1);
  917. }
  918. catch (std::exception& e) {
  919. span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  920. fprintf(stderr, "ERROR: Error initializing member: %s-%s %s\n", networkId.c_str(), memberId.c_str(), e.what());
  921. exit(-1);
  922. }
  923. }
  924. void CentralDB::heartbeat()
  925. {
  926. char publicId[1024];
  927. char hostnameTmp[1024];
  928. _myId.toString(false, publicId);
  929. if (gethostname(hostnameTmp, sizeof(hostnameTmp)) != 0) {
  930. hostnameTmp[0] = (char)0;
  931. }
  932. else {
  933. for (int i = 0; i < (int)sizeof(hostnameTmp); ++i) {
  934. if ((hostnameTmp[i] == '.') || (hostnameTmp[i] == 0)) {
  935. hostnameTmp[i] = (char)0;
  936. break;
  937. }
  938. }
  939. }
  940. const char* controllerId = _myAddressStr.c_str();
  941. const char* publicIdentity = publicId;
  942. const char* hostname = hostnameTmp;
  943. while (_run == 1) {
  944. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  945. auto tracer = provider->GetTracer("CentralDB");
  946. auto span = tracer->StartSpan("CentralDB::heartbeat");
  947. auto scope = tracer->WithActiveSpan(span);
  948. // fprintf(stderr, "%s: heartbeat\n", controllerId);
  949. auto c = _pool->borrow();
  950. int64_t ts = OSUtils::now();
  951. if (c->c) {
  952. std::string major = std::to_string(ZEROTIER_ONE_VERSION_MAJOR);
  953. std::string minor = std::to_string(ZEROTIER_ONE_VERSION_MINOR);
  954. std::string rev = std::to_string(ZEROTIER_ONE_VERSION_REVISION);
  955. std::string version = major + "." + minor + "." + rev;
  956. std::string versionStr = "v" + version;
  957. try {
  958. pqxx::work w { *c->c };
  959. w.exec(
  960. "INSERT INTO controllers_ctl (id, hostname, last_heartbeat, public_identity, version) VALUES "
  961. "($1, $2, TO_TIMESTAMP($3::double precision/1000), $4, $5) "
  962. "ON CONFLICT (id) DO UPDATE SET hostname = EXCLUDED.hostname, last_heartbeat = "
  963. "EXCLUDED.last_heartbeat, "
  964. "public_identity = EXCLUDED.public_identity, version = EXCLUDED.version",
  965. pqxx::params { controllerId, hostname, ts, publicIdentity, versionStr })
  966. .no_rows();
  967. w.commit();
  968. }
  969. catch (std::exception& e) {
  970. fprintf(stderr, "%s: Heartbeat update failed: %s\n", controllerId, e.what());
  971. span->End();
  972. std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  973. continue;
  974. }
  975. }
  976. _pool->unborrow(c);
  977. try {
  978. if (_listenerMode == LISTENER_MODE_REDIS && _redisMemberStatus) {
  979. if (_cc->redisConfig->clusterMode) {
  980. _cluster->zadd("controllers", "controllerId", ts);
  981. }
  982. else {
  983. _redis->zadd("controllers", "controllerId", ts);
  984. }
  985. }
  986. }
  987. catch (sw::redis::Error& e) {
  988. fprintf(stderr, "ERROR: Redis error in heartbeat thread: %s\n", e.what());
  989. }
  990. span->End();
  991. std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  992. }
  993. fprintf(stderr, "Exited heartbeat thread\n");
  994. }
  995. void CentralDB::commitThread()
  996. {
  997. fprintf(stderr, "%s: commitThread start\n", _myAddressStr.c_str());
  998. std::pair<nlohmann::json, bool> qitem;
  999. while (_commitQueue.get(qitem) & (_run == 1)) {
  1000. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  1001. auto tracer = provider->GetTracer("CentralDB");
  1002. auto span = tracer->StartSpan("CentralDB::commitThread");
  1003. auto scope = tracer->WithActiveSpan(span);
  1004. // fprintf(stderr, "commitThread tick\n");
  1005. if (! qitem.first.is_object()) {
  1006. fprintf(stderr, "not an object\n");
  1007. continue;
  1008. }
  1009. std::shared_ptr<PostgresConnection> c;
  1010. try {
  1011. c = _pool->borrow();
  1012. }
  1013. catch (std::exception& e) {
  1014. fprintf(stderr, "ERROR: %s\n", e.what());
  1015. continue;
  1016. }
  1017. if (! c) {
  1018. fprintf(stderr, "Error getting database connection\n");
  1019. continue;
  1020. }
  1021. Metrics::pgsql_commit_ticks++;
  1022. try {
  1023. nlohmann::json& config = (qitem.first);
  1024. const std::string objtype = config["objtype"];
  1025. if (objtype == "member") {
  1026. auto mspan = tracer->StartSpan("CentralDB::commitThread::member");
  1027. auto mscope = tracer->WithActiveSpan(mspan);
  1028. // fprintf(stderr, "%s: commitThread: member\n", _myAddressStr.c_str());
  1029. std::string memberId;
  1030. std::string networkId;
  1031. try {
  1032. pqxx::work w(*c->c);
  1033. memberId = config["id"];
  1034. networkId = config["nwid"];
  1035. std::string target = "NULL";
  1036. if (! config["remoteTraceTarget"].is_null()) {
  1037. target = config["remoteTraceTarget"];
  1038. }
  1039. // get network and the frontend it is assigned to
  1040. // if network does not exist, skip member update
  1041. pqxx::row nwrow =
  1042. w.exec(
  1043. "SELECT COUNT(id), frontend FROM networks_ctl WHERE id = $1 GROUP BY frontend",
  1044. pqxx::params { networkId })
  1045. .one_row();
  1046. int nwcount = nwrow[0].as<int>();
  1047. std::string frontend = nwrow[1].as<std::string>();
  1048. if (nwcount != 1) {
  1049. fprintf(stderr, "network %s does not exist. skipping member upsert\n", networkId.c_str());
  1050. w.abort();
  1051. _pool->unborrow(c);
  1052. continue;
  1053. }
  1054. pqxx::row mrow = w.exec(
  1055. "SELECT COUNT(device_id) FROM network_memberships_ctl WHERE device_id = $1 "
  1056. "AND network_id = $2",
  1057. pqxx::params { memberId, networkId })
  1058. .one_row();
  1059. int membercount = mrow[0].as<int>();
  1060. bool isNewMember = (membercount == 0);
  1061. std::string change_source;
  1062. if (! config["change_source"].is_null()) {
  1063. change_source = config["change_source"];
  1064. }
  1065. if (! isNewMember && change_source != "controller" && frontend != change_source) {
  1066. // if it is not a new member and the change source is not the controller and doesn't match the
  1067. // frontend, don't apply the change.
  1068. continue;
  1069. }
  1070. if (_listenerMode == LISTENER_MODE_PUBSUB) {
  1071. // Publish change to pubsub stream
  1072. if (config["change_source"].is_null() || config["change_source"] == "controller") {
  1073. nlohmann::json oldMember;
  1074. nlohmann::json newMember = config;
  1075. if (! isNewMember) {
  1076. oldMember = _getNetworkMember(w, networkId, memberId);
  1077. }
  1078. _changeNotifier->notifyMemberChange(oldMember, newMember, frontend);
  1079. }
  1080. }
  1081. pqxx::result res =
  1082. w.exec(
  1083. "INSERT INTO network_memberships_ctl (device_id, network_id, authorized, active_bridge, "
  1084. "ip_assignments, "
  1085. "no_auto_assign_ips, sso_exempt, authentication_expiry_time, capabilities, creation_time, "
  1086. "identity, last_authorized_time, last_deauthorized_time, "
  1087. "remote_trace_level, remote_trace_target, revision, tags, version_major, version_minor, "
  1088. "version_revision, version_protocol) "
  1089. "VALUES ($1, $2, $3, $4, $5, $6, $7, TO_TIMESTAMP($8::double precision/1000), $9, "
  1090. "TO_TIMESTAMP($10::double precision/1000), $11, TO_TIMESTAMP($12::double precision/1000), "
  1091. "TO_TIMESTAMP($13::double precision/1000), $14, $15, $16, $17, $18, $19, $20, $21) "
  1092. "ON CONFLICT (device_id, network_id) DO UPDATE SET "
  1093. "authorized = EXCLUDED.authorized, active_bridge = EXCLUDED.active_bridge, "
  1094. "ip_assignments = EXCLUDED.ip_assignments, no_auto_assign_ips = "
  1095. "EXCLUDED.no_auto_assign_ips, "
  1096. "sso_exempt = EXCLUDED.sso_exempt, authentication_expiry_time = "
  1097. "EXCLUDED.authentication_expiry_time, "
  1098. "capabilities = EXCLUDED.capabilities, creation_time = EXCLUDED.creation_time, "
  1099. "identity = EXCLUDED.identity, last_authorized_time = EXCLUDED.last_authorized_time, "
  1100. "last_deauthorized_time = EXCLUDED.last_deauthorized_time, "
  1101. "remote_trace_level = EXCLUDED.remote_trace_level, remote_trace_target = "
  1102. "EXCLUDED.remote_trace_target, "
  1103. "revision = EXCLUDED.revision, tags = EXCLUDED.tags, version_major = "
  1104. "EXCLUDED.version_major, "
  1105. "version_minor = EXCLUDED.version_minor, version_revision = EXCLUDED.version_revision, "
  1106. "version_protocol = EXCLUDED.version_protocol",
  1107. pqxx::params { memberId,
  1108. networkId,
  1109. (bool)config["authorized"],
  1110. (bool)config["activeBridge"],
  1111. config["ipAssignments"].get<std::vector<std::string> >(),
  1112. (bool)config["noAutoAssignIps"],
  1113. (bool)config["ssoExempt"],
  1114. (uint64_t)config["authenticationExpiryTime"],
  1115. OSUtils::jsonDump(config["capabilities"], -1),
  1116. (uint64_t)config["creationTime"],
  1117. OSUtils::jsonString(config["identity"], ""),
  1118. (uint64_t)config["lastAuthorizedTime"],
  1119. (uint64_t)config["lastDeauthorizedTime"],
  1120. (int)config["remoteTraceLevel"],
  1121. target,
  1122. (uint64_t)config["revision"],
  1123. OSUtils::jsonDump(config["tags"], -1),
  1124. (int)config["vMajor"],
  1125. (int)config["vMinor"],
  1126. (int)config["vRev"],
  1127. (int)config["vProto"] })
  1128. .no_rows();
  1129. w.commit();
  1130. if (_smee != NULL && isNewMember) {
  1131. // TODO: Smee Notifications for New Members
  1132. // pqxx::row row = w.exec_params1(
  1133. // "SELECT "
  1134. // " count(h.hook_id) "
  1135. // "FROM "
  1136. // " ztc_hook h "
  1137. // " INNER JOIN ztc_org o ON o.org_id = h.org_id "
  1138. // " INNER JOIN ztc_network n ON n.owner_id = o.owner_id "
  1139. // " WHERE "
  1140. // "n.id = $1 ",
  1141. // networkId);
  1142. // int64_t hookCount = row[0].as<int64_t>();
  1143. // if (hookCount > 0) {
  1144. // notifyNewMember(networkId, memberId);
  1145. // }
  1146. }
  1147. const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL);
  1148. const uint64_t memberidInt = OSUtils::jsonIntHex(config["id"], 0ULL);
  1149. if (nwidInt && memberidInt) {
  1150. nlohmann::json nwOrig;
  1151. nlohmann::json memOrig;
  1152. nlohmann::json memNew(config);
  1153. get(nwidInt, nwOrig, memberidInt, memOrig);
  1154. _memberChanged(memOrig, memNew, qitem.second);
  1155. }
  1156. else {
  1157. fprintf(
  1158. stderr, "%s: Can't notify of change. Error parsing nwid or memberid: %llu-%llu\n",
  1159. _myAddressStr.c_str(), (unsigned long long)nwidInt, (unsigned long long)memberidInt);
  1160. }
  1161. }
  1162. catch (std::exception& e) {
  1163. fprintf(
  1164. stderr, "%s ERROR: Error updating member %s-%s: %s\n", _myAddressStr.c_str(), networkId.c_str(),
  1165. memberId.c_str(), e.what());
  1166. mspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1167. }
  1168. }
  1169. else if (objtype == "network") {
  1170. auto nspan = tracer->StartSpan("CentralDB::commitThread::network");
  1171. auto nscope = tracer->WithActiveSpan(nspan);
  1172. try {
  1173. // fprintf(stderr, "%s: commitThread: network\n", _myAddressStr.c_str());
  1174. pqxx::work w(*c->c);
  1175. std::string id = config["id"];
  1176. pqxx::row nwrow =
  1177. w.exec(
  1178. "SELECT COUNT(id), frontend FROM networks_ctl WHERE id = $1 GROUP BY frontend",
  1179. pqxx::params { id })
  1180. .one_row();
  1181. int nwcount = nwrow[0].as<int>();
  1182. std::string frontend = nwrow[1].as<std::string>();
  1183. bool isNewNetwork = (nwcount == 0);
  1184. std::string change_source;
  1185. if (! config["change_source"].is_null()) {
  1186. change_source = config["change_source"];
  1187. }
  1188. if (! isNewNetwork && change_source != "controller" && frontend != change_source) {
  1189. // if it is not a new network and the change source is not the controller and doesn't match the
  1190. // frontend, don't apply the change.
  1191. continue;
  1192. }
  1193. if (_listenerMode == LISTENER_MODE_PUBSUB) {
  1194. // Publish change to pubsub stream
  1195. if (config["change_source"].is_null() || config["change_source"] == "controller") {
  1196. nlohmann::json oldNetwork;
  1197. nlohmann::json newNetwork = config;
  1198. if (! isNewNetwork) {
  1199. oldNetwork = _getNetwork(w, id);
  1200. }
  1201. _changeNotifier->notifyNetworkChange(oldNetwork, newNetwork, frontend);
  1202. }
  1203. }
  1204. pqxx::result res = w.exec(
  1205. "INSERT INTO networks_ctl (id, name, configuration, controller_id, revision, frontend) "
  1206. "VALUES ($1, $2, $3, $4, $5, $6) "
  1207. "ON CONFLICT (id) DO UPDATE SET "
  1208. "name = EXCLUDED.name, configuration = EXCLUDED.configuration, revision = EXCLUDED.revision+1, "
  1209. "frontend = EXCLUDED.frontend",
  1210. pqxx::params { id, OSUtils::jsonString(config["name"], ""), OSUtils::jsonDump(config, -1),
  1211. _myAddressStr, ((uint64_t)config["revision"]), change_source });
  1212. w.commit();
  1213. const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL);
  1214. if (nwidInt) {
  1215. nlohmann::json nwOrig;
  1216. nlohmann::json nwNew(config);
  1217. get(nwidInt, nwOrig);
  1218. _networkChanged(nwOrig, nwNew, qitem.second);
  1219. }
  1220. else {
  1221. fprintf(
  1222. stderr, "%s: Can't notify network changed: %llu\n", _myAddressStr.c_str(),
  1223. (unsigned long long)nwidInt);
  1224. }
  1225. }
  1226. catch (std::exception& e) {
  1227. nspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1228. fprintf(stderr, "%s ERROR: Error updating network: %s\n", _myAddressStr.c_str(), e.what());
  1229. }
  1230. if (_listenerMode == LISTENER_MODE_REDIS && _redisMemberStatus) {
  1231. try {
  1232. std::string id = config["id"];
  1233. std::string controllerId = _myAddressStr.c_str();
  1234. std::string key = "networks:{" + controllerId + "}";
  1235. if (_cc->redisConfig->clusterMode) {
  1236. _cluster->sadd(key, id);
  1237. }
  1238. else {
  1239. _redis->sadd(key, id);
  1240. }
  1241. }
  1242. catch (sw::redis::Error& e) {
  1243. nspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1244. fprintf(stderr, "ERROR: Error adding network to Redis: %s\n", e.what());
  1245. }
  1246. }
  1247. }
  1248. else if (objtype == "_delete_network") {
  1249. auto dspan = tracer->StartSpan("CentralDB::commitThread::_delete_network");
  1250. auto dscope = tracer->WithActiveSpan(dspan);
  1251. // fprintf(stderr, "%s: commitThread: delete network\n", _myAddressStr.c_str());
  1252. try {
  1253. pqxx::work w(*c->c);
  1254. std::string networkId = config["id"];
  1255. fprintf(stderr, "Deleting network %s\n", networkId.c_str());
  1256. w.exec("DELETE FROM network_memberships_ctl WHERE network_id = $1", pqxx::params { networkId });
  1257. w.exec("DELETE FROM networks_ctl WHERE id = $1", pqxx::params { networkId });
  1258. w.commit();
  1259. uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL);
  1260. json oldConfig;
  1261. get(nwidInt, oldConfig);
  1262. json empty;
  1263. _networkChanged(oldConfig, empty, qitem.second);
  1264. }
  1265. catch (std::exception& e) {
  1266. dspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1267. fprintf(stderr, "%s ERROR: Error deleting network: %s\n", _myAddressStr.c_str(), e.what());
  1268. }
  1269. if (_listenerMode == LISTENER_MODE_REDIS && _redisMemberStatus) {
  1270. try {
  1271. std::string id = config["id"];
  1272. std::string controllerId = _myAddressStr.c_str();
  1273. std::string key = "networks:{" + controllerId + "}";
  1274. if (_cc->redisConfig->clusterMode) {
  1275. _cluster->srem(key, id);
  1276. _cluster->del("network-nodes-online:{" + controllerId + "}:" + id);
  1277. }
  1278. else {
  1279. _redis->srem(key, id);
  1280. _redis->del("network-nodes-online:{" + controllerId + "}:" + id);
  1281. }
  1282. }
  1283. catch (sw::redis::Error& e) {
  1284. dspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1285. fprintf(stderr, "ERROR: Error adding network to Redis: %s\n", e.what());
  1286. }
  1287. }
  1288. }
  1289. else if (objtype == "_delete_member") {
  1290. auto mspan = tracer->StartSpan("CentralDB::commitThread::_delete_member");
  1291. auto mscope = tracer->WithActiveSpan(mspan);
  1292. // fprintf(stderr, "%s commitThread: delete member\n", _myAddressStr.c_str());
  1293. try {
  1294. pqxx::work w(*c->c);
  1295. std::string memberId = config["id"];
  1296. std::string networkId = config["nwid"];
  1297. pqxx::result res =
  1298. w.exec(
  1299. "DELETE FROM network_memberships_ctl WHERE device_id = $1 AND network_id = $2",
  1300. pqxx::params { memberId, networkId })
  1301. .no_rows();
  1302. w.commit();
  1303. uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL);
  1304. uint64_t memberidInt = OSUtils::jsonIntHex(config["id"], 0ULL);
  1305. nlohmann::json networkConfig;
  1306. nlohmann::json oldConfig;
  1307. get(nwidInt, networkConfig, memberidInt, oldConfig);
  1308. json empty;
  1309. _memberChanged(oldConfig, empty, qitem.second);
  1310. }
  1311. catch (std::exception& e) {
  1312. mspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1313. fprintf(stderr, "%s ERROR: Error deleting member: %s\n", _myAddressStr.c_str(), e.what());
  1314. }
  1315. if (_listenerMode == LISTENER_MODE_REDIS && _redisMemberStatus) {
  1316. try {
  1317. std::string memberId = config["id"];
  1318. std::string networkId = config["nwid"];
  1319. std::string controllerId = _myAddressStr.c_str();
  1320. std::string key = "network-nodes-all:{" + controllerId + "}:" + networkId;
  1321. if (_cc->redisConfig->clusterMode) {
  1322. _cluster->srem(key, memberId);
  1323. _cluster->del("member:{" + controllerId + "}:" + networkId + ":" + memberId);
  1324. }
  1325. else {
  1326. _redis->srem(key, memberId);
  1327. _redis->del("member:{" + controllerId + "}:" + networkId + ":" + memberId);
  1328. }
  1329. }
  1330. catch (sw::redis::Error& e) {
  1331. mspan->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1332. fprintf(stderr, "ERROR: Error deleting member from Redis: %s\n", e.what());
  1333. }
  1334. }
  1335. }
  1336. else {
  1337. fprintf(stderr, "%s ERROR: unknown objtype\n", _myAddressStr.c_str());
  1338. }
  1339. }
  1340. catch (std::exception& e) {
  1341. span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
  1342. fprintf(stderr, "%s ERROR: Error getting objtype: %s\n", _myAddressStr.c_str(), e.what());
  1343. }
  1344. _pool->unborrow(c);
  1345. c.reset();
  1346. }
  1347. fprintf(stderr, "%s commitThread finished\n", _myAddressStr.c_str());
  1348. }
  1349. void CentralDB::notifyNewMember(const std::string& networkID, const std::string& memberID)
  1350. {
  1351. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  1352. auto tracer = provider->GetTracer("CentralDB");
  1353. auto span = tracer->StartSpan("CentralDB::notifyNewMember");
  1354. auto scope = tracer->WithActiveSpan(span);
  1355. rustybits::smee_client_notify_network_joined(_smee, networkID.c_str(), memberID.c_str());
  1356. }
  1357. void CentralDB::onlineNotificationThread()
  1358. {
  1359. waitForReady();
  1360. while (_run == 1) {
  1361. auto provider = opentelemetry::trace::Provider::GetTracerProvider();
  1362. auto tracer = provider->GetTracer("CentralDB");
  1363. auto span = tracer->StartSpan("CentralDB::onlineNotificationThread");
  1364. auto scope = tracer->WithActiveSpan(span);
  1365. try {
  1366. std::unordered_map<std::pair<uint64_t, uint64_t>, NodeOnlineRecord, _PairHasher> lastOnline;
  1367. {
  1368. std::lock_guard<std::mutex> l(_lastOnline_l);
  1369. lastOnline.swap(_lastOnline);
  1370. }
  1371. uint64_t updateCount = 0;
  1372. auto c = _pool->borrow();
  1373. pqxx::work w(*c->c);
  1374. for (auto i = lastOnline.begin(); i != lastOnline.end(); ++i) {
  1375. updateCount += 1;
  1376. uint64_t nwid_i = i->first.first;
  1377. char nwidTmp[64];
  1378. char memTmp[64];
  1379. char ipTmp[64];
  1380. OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i);
  1381. OSUtils::ztsnprintf(memTmp, sizeof(memTmp), "%.10llx", i->first.second);
  1382. nlohmann::json network, member;
  1383. if (! get(nwid_i, network, i->first.second, member)) {
  1384. continue; // skip non existent networks/members
  1385. }
  1386. std::string networkId(nwidTmp);
  1387. std::string memberId(memTmp);
  1388. try {
  1389. // check if the member exists first.
  1390. //
  1391. // exec_params1 will throw pqxx::unexpected_rows if not exactly one row is returned. If that's the
  1392. // case, skip this record and move on.
  1393. pqxx::row r = w.exec(
  1394. "SELECT device_id, network_id FROM network_memberships_ctl WHERE network_id = "
  1395. "$1 AND device_id "
  1396. "= $2",
  1397. pqxx::params { networkId, memberId })
  1398. .one_row();
  1399. }
  1400. catch (pqxx::unexpected_rows& e) {
  1401. continue;
  1402. }
  1403. int64_t ts = i->second.lastSeen;
  1404. std::string ipAddr = i->second.physicalAddress.toIpString(ipTmp);
  1405. std::string timestamp = std::to_string(ts);
  1406. std::string osArch = i->second.osArch;
  1407. std::vector<std::string> osArchSplit = split(osArch, '/');
  1408. std::string os = "unknown";
  1409. std::string arch = "unknown";
  1410. std::string frontend = member["frontend"].get<std::string>();
  1411. int vMajor = OSUtils::jsonInt(member["vMajor"], 0);
  1412. int vMinor = OSUtils::jsonInt(member["vMinor"], 0);
  1413. int vRev = OSUtils::jsonInt(member["vRev"], 0);
  1414. std::string version;
  1415. if (vMajor <= 0 && vMinor <= 0 && vRev <= 0) {
  1416. vMajor = 0;
  1417. vMinor = 0;
  1418. vRev = 0;
  1419. version = "unknown";
  1420. }
  1421. else {
  1422. version = "v" + std::to_string(vMajor) + "." + std::to_string(vMinor) + "." + std::to_string(vRev);
  1423. }
  1424. if (osArchSplit.size() == 2) {
  1425. os = osArchSplit[0];
  1426. arch = osArchSplit[1];
  1427. }
  1428. _statusWriter->updateNodeStatus(
  1429. networkId, memberId, os, arch, version, i->second.physicalAddress, ts, frontend);
  1430. }
  1431. _statusWriter->writePending();
  1432. w.commit();
  1433. _pool->unborrow(c);
  1434. }
  1435. catch (std::exception& e) {
  1436. fprintf(stderr, "%s: error in onlinenotification thread: %s\n", _myAddressStr.c_str(), e.what());
  1437. }
  1438. std::this_thread::sleep_for(std::chrono::seconds(10));
  1439. }
  1440. }
  1441. nlohmann::json CentralDB::_getNetworkMember(pqxx::work& tx, const std::string networkID, const std::string memberID)
  1442. {
  1443. nlohmann::json out;
  1444. try {
  1445. pqxx::row row =
  1446. tx.exec(
  1447. "SELECT nm.device_id, nm.network_id, nm.authorized, nm.active_bridge, nm.ip_assignments, "
  1448. "nm.no_auto_assign_ips, "
  1449. "nm.sso_exempt, (EXTRACT(EPOCH FROM nm.authentication_expiry_time AT TIME ZONE 'UTC')*1000)::bigint, "
  1450. "(EXTRACT(EPOCH FROM nm.creation_time AT TIME ZONE 'UTC')*1000)::bigint, nm.identity, "
  1451. "(EXTRACT(EPOCH FROM nm.last_authorized_time AT TIME ZONE 'UTC')*1000)::bigint, "
  1452. "(EXTRACT(EPOCH FROM nm.last_deauthorized_time AT TIME ZONE 'UTC')*1000)::bigint, "
  1453. "nm.remote_trace_level, nm.remote_trace_target, nm.revision, nm.capabilities, nm.tags, "
  1454. "nm.frontend "
  1455. "FROM network_memberships_ctl nm "
  1456. "INNER JOIN networks_ctl n "
  1457. " ON nm.network_id = n.id "
  1458. "WHERE nm.network_id = $1 AND nm.device_id = $2",
  1459. pqxx::params { networkID, memberID })
  1460. .one_row();
  1461. bool authorized = row[2].as<bool>();
  1462. std::optional<bool> active_bridge =
  1463. row[3].is_null() ? std::optional<bool>() : std::optional<bool>(row[3].as<bool>());
  1464. std::string ip_assignments = row[4].is_null() ? "{}" : row[4].as<std::string>();
  1465. std::optional<bool> no_auto_assign_ips =
  1466. row[5].is_null() ? std::optional<bool>() : std::optional<bool>(row[5].as<bool>());
  1467. std::optional<bool> sso_exempt =
  1468. row[6].is_null() ? std::optional<bool>() : std::optional<bool>(row[6].as<bool>());
  1469. std::optional<uint64_t> authentication_expiry_time =
  1470. row[7].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[7].as<uint64_t>());
  1471. std::optional<uint64_t> creation_time =
  1472. row[8].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[8].as<uint64_t>());
  1473. std::optional<std::string> identity =
  1474. row[9].is_null() ? std::optional<std::string>() : std::optional<std::string>(row[9].as<std::string>());
  1475. std::optional<uint64_t> last_authorized_time =
  1476. row[10].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[10].as<uint64_t>());
  1477. std::optional<uint64_t> last_deauthorized_time =
  1478. row[11].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[11].as<uint64_t>());
  1479. std::optional<int32_t> remote_trace_level =
  1480. row[12].is_null() ? std::optional<int32_t>() : std::optional<int32_t>(row[12].as<int32_t>());
  1481. std::optional<std::string> remote_trace_target =
  1482. row[13].is_null() ? std::optional<std::string>() : std::optional<std::string>(row[13].as<std::string>());
  1483. std::optional<uint64_t> revision =
  1484. row[14].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[14].as<uint64_t>());
  1485. std::optional<std::string> capabilities =
  1486. row[15].is_null() ? std::optional<std::string>() : std::optional<std::string>(row[15].as<std::string>());
  1487. std::optional<std::string> tags =
  1488. row[16].is_null() ? std::optional<std::string>() : std::optional<std::string>(row[16].as<std::string>());
  1489. std::string frontend = row[17].is_null() ? "" : row[17].as<std::string>();
  1490. out["objtype"] = "member";
  1491. out["id"] = memberID;
  1492. out["nwid"] = networkID;
  1493. out["address"] = identity.value_or("");
  1494. out["authorized"] = authorized;
  1495. out["activeBridge"] = active_bridge.value_or(false);
  1496. out["ipAssignments"] = json::array();
  1497. if (ip_assignments != "{}" && ip_assignments != "[]") {
  1498. std::string tmp = ip_assignments.substr(1, ip_assignments.length() - 2);
  1499. std::vector<std::string> addrs = split(tmp, ',');
  1500. for (auto it = addrs.begin(); it != addrs.end(); ++it) {
  1501. out["ipAssignments"].push_back(*it);
  1502. }
  1503. }
  1504. out["capabilities"] = json::parse(capabilities.value_or("[]"));
  1505. out["creationTime"] = creation_time.value_or(0);
  1506. out["lastAuthorizedTime"] = last_authorized_time.value_or(0);
  1507. out["lastDeauthorizedTime"] = last_deauthorized_time.value_or(0);
  1508. out["noAutoAssignIps"] = no_auto_assign_ips.value_or(false);
  1509. out["remoteTraceLevel"] = remote_trace_level.value_or(0);
  1510. out["remoteTraceTarget"] = remote_trace_target.value_or(nullptr);
  1511. out["revision"] = revision.value_or(0);
  1512. out["ssoExempt"] = sso_exempt.value_or(false);
  1513. out["authenticationExpiryTime"] = authentication_expiry_time.value_or(0);
  1514. out["tags"] = json::parse(tags.value_or("[]"));
  1515. out["frontend"] = frontend;
  1516. }
  1517. catch (std::exception& e) {
  1518. fprintf(
  1519. stderr, "ERROR: Error getting network member %s-%s: %s\n", networkID.c_str(), memberID.c_str(), e.what());
  1520. return nlohmann::json();
  1521. }
  1522. return out;
  1523. }
  1524. nlohmann::json CentralDB::_getNetwork(pqxx::work& tx, const std::string networkID)
  1525. {
  1526. nlohmann::json out;
  1527. try {
  1528. std::optional<std::string> name;
  1529. std::string cfg;
  1530. std::optional<uint64_t> creation_time;
  1531. std::optional<uint64_t> last_modified;
  1532. std::optional<uint64_t> revision;
  1533. std::string frontend;
  1534. pqxx::row row = tx.exec(
  1535. "SELECT id, name, configuration , (EXTRACT(EPOCH FROM creation_time AT TIME ZONE "
  1536. "'UTC')*1000)::bigint, "
  1537. "(EXTRACT(EPOCH FROM last_modified AT TIME ZONE 'UTC')*1000)::bigint, revision, frontend "
  1538. "FROM networks_ctl WHERE id = $1",
  1539. pqxx::params { networkID })
  1540. .one_row();
  1541. cfg = row[2].as<std::string>();
  1542. creation_time = row[3].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[3].as<uint64_t>());
  1543. last_modified = row[4].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[4].as<uint64_t>());
  1544. revision = row[5].is_null() ? std::optional<uint64_t>() : std::optional<uint64_t>(row[5].as<uint64_t>());
  1545. frontend = row[6].is_null() ? "" : row[6].as<std::string>();
  1546. nlohmann::json cfgtmp = nlohmann::json::parse(cfg);
  1547. if (! cfgtmp.is_object()) {
  1548. fprintf(stderr, "ERROR: Network %s configuration is not a JSON object\n", networkID.c_str());
  1549. return nlohmann::json();
  1550. }
  1551. out["objtype"] = "network";
  1552. out["id"] = row[0].as<std::string>();
  1553. out["name"] = row[1].is_null() ? "" : row[1].as<std::string>();
  1554. out["creationTime"] = creation_time.value_or(0);
  1555. out["lastModified"] = last_modified.value_or(0);
  1556. out["revision"] = revision.value_or(0);
  1557. out["capabilities"] = cfgtmp["capabilities"].is_array() ? cfgtmp["capabilities"] : json::array();
  1558. out["enableBroadcast"] = cfgtmp["enableBroadcast"].is_boolean() ? cfgtmp["enableBroadcast"].get<bool>() : false;
  1559. out["mtu"] = cfgtmp["mtu"].is_number() ? cfgtmp["mtu"].get<int32_t>() : 2800;
  1560. out["multicastLimit"] = cfgtmp["multicastLimit"].is_number() ? cfgtmp["multicastLimit"].get<int32_t>() : 64;
  1561. out["private"] = cfgtmp["private"].is_boolean() ? cfgtmp["private"].get<bool>() : true;
  1562. out["remoteTraceLevel"] =
  1563. cfgtmp["remoteTraceLevel"].is_number() ? cfgtmp["remoteTraceLevel"].get<int32_t>() : 0;
  1564. out["remoteTraceTarget"] =
  1565. cfgtmp["remoteTraceTarget"].is_string() ? cfgtmp["remoteTraceTarget"].get<std::string>() : "";
  1566. out["revision"] = revision.value_or(0);
  1567. out["rules"] = cfgtmp["rules"].is_array() ? cfgtmp["rules"] : json::array();
  1568. out["tags"] = cfgtmp["tags"].is_array() ? cfgtmp["tags"] : json::array();
  1569. if (cfgtmp["v4AssignMode"].is_object()) {
  1570. out["v4AssignMode"] = cfgtmp["v4AssignMode"];
  1571. }
  1572. else {
  1573. out["v4AssignMode"] = json::object();
  1574. out["v4AssignMode"]["zt"] = true;
  1575. }
  1576. if (cfgtmp["v6AssignMode"].is_object()) {
  1577. out["v6AssignMode"] = cfgtmp["v6AssignMode"];
  1578. }
  1579. else {
  1580. out["v6AssignMode"] = json::object();
  1581. out["v6AssignMode"]["zt"] = true;
  1582. out["v6AssignMode"]["6plane"] = true;
  1583. out["v6AssignMode"]["rfc4193"] = false;
  1584. }
  1585. out["ssoEnabled"] = cfgtmp["ssoEnabled"].is_boolean() ? cfgtmp["ssoEnabled"].get<bool>() : false;
  1586. out["objtype"] = "network";
  1587. out["routes"] = cfgtmp["routes"].is_array() ? cfgtmp["routes"] : json::array();
  1588. out["clientId"] = cfgtmp["clientId"].is_string() ? cfgtmp["clientId"].get<std::string>() : "";
  1589. out["authorizationEndpoint"] =
  1590. cfgtmp["authorizationEndpoint"].is_string() ? cfgtmp["authorizationEndpoint"].get<std::string>() : nullptr;
  1591. out["provider"] = cfgtmp["ssoProvider"].is_string() ? cfgtmp["ssoProvider"].get<std::string>() : "";
  1592. if (! cfgtmp["dns"].is_object()) {
  1593. cfgtmp["dns"] = json::object();
  1594. cfgtmp["dns"]["domain"] = "";
  1595. cfgtmp["dns"]["servers"] = json::array();
  1596. }
  1597. else {
  1598. out["dns"] = cfgtmp["dns"];
  1599. }
  1600. out["ipAssignmentPools"] = cfgtmp["ipAssignmentPools"].is_array() ? cfgtmp["ipAssignmentPools"] : json::array();
  1601. out["frontend"] = row[6].as<std::string>();
  1602. }
  1603. catch (std::exception& e) {
  1604. fprintf(stderr, "ERROR: Error getting network %s: %s\n", networkID.c_str(), e.what());
  1605. return nlohmann::json();
  1606. }
  1607. return out;
  1608. }
  1609. #endif // ZT_CONTROLLER_USE_LIBPQ