From 464bca5d20068bcf37acf496ac941efc908c248b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 25 Aug 2023 19:03:03 -0400 Subject: [PATCH] 1.12.1 (#2112) * Fix primary port binding issue in 1.12 (#2107) * Add test for primary port bindings to validator - See #2105 * Add delay to binding test * Remove TCP binding logic from Binder to fix #2105 * add second control plane socket for ipv6 * fix controller network post endpoint * exit if we can't bind at least one of IPV4 or IPV6 for control plane port --------- Co-authored-by: Grant Limberg * Version bump, Linux version stuff, Debian dependencies from 1.12.0 rebuild, release notes. * macOS version bump in installer * Windows version bump. --------- Co-authored-by: Joseph Henry Co-authored-by: Grant Limberg --- .github/workflows/validate-linux.sh | 42 ++++- RELEASE-NOTES.md | 6 + controller/EmbeddedNetworkController.cpp | 74 ++++++-- controller/EmbeddedNetworkController.hpp | 1 + debian/changelog | 6 + debian/control | 2 +- debian/control.wheezy | 2 +- debian/rules | 0 debian/rules.wheezy | 0 ext/installfiles/mac/ZeroTier One.pkgproj | 2 +- ext/installfiles/windows/ZeroTier One.aip | 8 +- osdep/Binder.hpp | 20 +-- service/OneService.cpp | 205 ++++++++++++++++------ version.h | 2 +- zerotier-one.spec | 5 +- 15 files changed, 274 insertions(+), 101 deletions(-) mode change 100755 => 100644 debian/rules mode change 100755 => 100644 debian/rules.wheezy diff --git a/.github/workflows/validate-linux.sh b/.github/workflows/validate-linux.sh index 61670d670..a661f6f6c 100755 --- a/.github/workflows/validate-linux.sh +++ b/.github/workflows/validate-linux.sh @@ -20,6 +20,9 @@ mkdir $TEST_DIR_PREFIX # How long we will wait for ZT to come online before considering it a failure MAX_WAIT_SECS=30 +ZT_PORT_NODE_1=9996 +ZT_PORT_NODE_2=9997 + ################################################################################ # Multi-node connectivity and performance test # ################################################################################ @@ -99,14 +102,19 @@ test() { --xml=yes \ --xml-file=$FILENAME_MEMORY_LOG \ --leak-check=full \ - ./zerotier-one node1 -p9996 -U >>node_1.log 2>&1 & + ./zerotier-one node1 -p$ZT_PORT_NODE_1 -U >>node_1.log 2>&1 & # Second instance, not run in memory profiler # Don't set up internet access until _after_ zerotier is running # This has been a source of stuckness in the past. $NS2 ip addr del 192.168.1.2/24 dev veth3 - $NS2 sudo ./zerotier-one node2 -U -p9997 >>node_2.log 2>&1 & - sleep 1; + $NS2 sudo ./zerotier-one node2 -U -p$ZT_PORT_NODE_2 >>node_2.log 2>&1 & + + sleep 10; # New HTTP control plane is a bit sluggish, so we delay here + + check_bind_to_correct_ports $ZT_PORT_NODE_1 + check_bind_to_correct_ports $ZT_PORT_NODE_2 + $NS2 ip addr add 192.168.1.2/24 dev veth3 $NS2 ip route add default via 192.168.1.1 @@ -458,4 +466,32 @@ check_exit_on_invalid_identity() { fi } +################################################################################ +# Check that we're binding to the primary port for TCP/TCP6/UDP # +################################################################################ + +check_bind_to_correct_ports() { + PORT_NUMBER=$1 + echo "Checking bound ports:" + sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" + if [[ $(sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" | grep "tcp") ]]; + then + : + else + exit_test_and_generate_report $TEST_FAIL "ZeroTier did not bind to tcp/$1" + fi + if [[ $(sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" | grep "tcp6") ]]; + then + : + else + exit_test_and_generate_report $TEST_FAIL "ZeroTier did not bind to tcp6/$1" + fi + if [[ $(sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" | grep "udp") ]]; + then + : + else + exit_test_and_generate_report $TEST_FAIL "ZeroTier did not bind to udp/$1" + fi +} + test "$@" diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 1585aeead..86f3f1e8c 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,12 @@ ZeroTier Release Notes ====== +# 2023-08-25 -- Version 1.12.1 + + * Minor release to fix a port binding issue in Linux. + * Update Debian dependencies. + * No changes for other platforms. + # 2023-08-23 -- Version 1.12.0 * Experimental Windows ARM64 support diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index b60c375c4..ac1947ce4 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -863,9 +863,17 @@ std::string EmbeddedNetworkController::networkUpdateFromPostData(uint64_t networ void EmbeddedNetworkController::configureHTTPControlPlane( httplib::Server &s, + httplib::Server &sv6, const std::function setContent) { - s.Get("/controller/network", [&, setContent](const httplib::Request &req, httplib::Response &res) { + // Control plane Endpoints + std::string networkListPath = "/controller/network"; + std::string networkPath = "/controller/network/([0-9a-fA-F]{16})"; + std::string oldAndBustedNetworkCreatePath = "/controller/network/([0-9a-fA-F]{10})______"; + std::string memberListPath = "/controller/network/([0-9a-fA-F]{16})/member"; + std::string memberPath = "/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})"; + + auto networkListGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { std::set networkIds; _db.networks(networkIds); char tmp[64]; @@ -877,9 +885,11 @@ void EmbeddedNetworkController::configureHTTPControlPlane( } setContent(req, res, out.dump()); - }); + }; + s.Get(networkListPath, networkListGet); + sv6.Get(networkListPath, networkListGet); - s.Get("/controller/network/([0-9a-fA-F]{16})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto networkGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto networkID = req.matches[1]; uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); json network; @@ -889,7 +899,9 @@ void EmbeddedNetworkController::configureHTTPControlPlane( } setContent(req, res, network.dump()); - }); + }; + s.Get(networkPath, networkGet); + sv6.Get(networkPath, networkGet); auto createNewNetwork = [&, setContent](const httplib::Request &req, httplib::Response &res) { fprintf(stderr, "creating new network (new style)\n"); @@ -912,8 +924,10 @@ void EmbeddedNetworkController::configureHTTPControlPlane( setContent(req, res, networkUpdateFromPostData(nwid, req.body)); }; - s.Put("/controller/network", createNewNetwork); - s.Post("/controller/network", createNewNetwork); + s.Put(networkListPath, createNewNetwork); + s.Post(networkListPath, createNewNetwork); + sv6.Put(networkListPath, createNewNetwork); + sv6.Post(networkListPath, createNewNetwork); auto createNewNetworkOldAndBusted = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto inID = req.matches[1].str(); @@ -941,10 +955,24 @@ void EmbeddedNetworkController::configureHTTPControlPlane( } setContent(req, res, networkUpdateFromPostData(nwid, req.body)); }; - s.Put("/controller/network/([0-9a-fA-F]{10})______", createNewNetworkOldAndBusted); - s.Post("/controller/network/([0-9a-fA-F]{10})______", createNewNetworkOldAndBusted); + s.Put(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); + s.Post(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); + sv6.Put(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); + sv6.Post(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); + + auto networkPost = [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto networkID = req.matches[1].str(); + uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); + + res.status = 200; + setContent(req, res, networkUpdateFromPostData(nwid, req.body)); + }; + s.Put(networkPath, networkPost); + s.Post(networkPath, networkPost); + sv6.Put(networkPath, networkPost); + sv6.Post(networkPath, networkPost); - s.Delete("/controller/network/([0-9a-fA-F]{16})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto networkDelete = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto networkID = req.matches[1].str(); uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); @@ -956,9 +984,11 @@ void EmbeddedNetworkController::configureHTTPControlPlane( _db.eraseNetwork(nwid); setContent(req, res, network.dump()); - }); + }; + s.Delete(networkPath, networkDelete); + sv6.Delete(networkPath, networkDelete); - s.Get("/controller/network/([0-9a-fA-F]{16})/member", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto memberListGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto networkID = req.matches[1]; uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); json network; @@ -982,9 +1012,11 @@ void EmbeddedNetworkController::configureHTTPControlPlane( } setContent(req, res, out.dump()); - }); + }; + s.Get(memberListPath, memberListGet); + sv6.Get(memberListPath, memberListGet); - s.Get("/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto memberGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto networkID = req.matches[1]; auto memberID = req.matches[2]; uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); @@ -997,7 +1029,9 @@ void EmbeddedNetworkController::configureHTTPControlPlane( } setContent(req, res, member.dump()); - }); + }; + s.Get(memberPath, memberGet); + sv6.Get(memberPath, memberGet); auto memberPost = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto networkID = req.matches[1].str(); @@ -1102,10 +1136,12 @@ void EmbeddedNetworkController::configureHTTPControlPlane( setContent(req, res, member.dump()); }; - s.Put("/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})", memberPost); - s.Post("/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})", memberPost); + s.Put(memberPath, memberPost); + s.Post(memberPath, memberPost); + sv6.Put(memberPath, memberPost); + sv6.Post(memberPath, memberPost); - s.Delete("/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto memberDelete = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto networkID = req.matches[1].str(); auto memberID = req.matches[2].str(); @@ -1126,7 +1162,9 @@ void EmbeddedNetworkController::configureHTTPControlPlane( _db.eraseMember(nwid, address); setContent(req, res, member.dump()); - }); + }; + s.Delete(memberPath, memberDelete); + sv6.Delete(memberPath, memberDelete); } void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt) diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index ef369be39..4ea00b65a 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -70,6 +70,7 @@ class EmbeddedNetworkController : public NetworkController,public DB::ChangeList void configureHTTPControlPlane( httplib::Server &s, + httplib::Server &sV6, const std::function); void handleRemoteTrace(const ZT_RemoteTrace &rt); diff --git a/debian/changelog b/debian/changelog index e03a94a20..0ba464399 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +zerotier-one (1.12.1) unstable; urgency=medium + + * See RELEASE-NOTES.md for release notes. + + -- Adam Ierymenko Fri, 25 Aug 2023 01:00:00 -0700 + zerotier-one (1.12.0) unstable; urgency=medium * See RELEASE-NOTES.md for release notes. diff --git a/debian/control b/debian/control index 195acefd7..b0c178ab1 100644 --- a/debian/control +++ b/debian/control @@ -10,7 +10,7 @@ Homepage: https://www.zerotier.com/ Package: zerotier-one Architecture: any -Depends: iproute2, adduser, libstdc++6 (>= 5), openssl +Depends: adduser, libstdc++6 (>= 5), openssl Homepage: https://www.zerotier.com/ Description: ZeroTier network virtualization service ZeroTier One lets you join ZeroTier virtual networks and diff --git a/debian/control.wheezy b/debian/control.wheezy index a8e240847..fbbc40d93 100644 --- a/debian/control.wheezy +++ b/debian/control.wheezy @@ -10,7 +10,7 @@ Homepage: https://www.zerotier.com/ Package: zerotier-one Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, iproute, libstdc++6 +Depends: ${shlibs:Depends}, ${misc:Depends}, libstdc++6 Homepage: https://www.zerotier.com/ Description: ZeroTier network virtualization service ZeroTier One lets you join ZeroTier virtual networks and diff --git a/debian/rules b/debian/rules old mode 100755 new mode 100644 diff --git a/debian/rules.wheezy b/debian/rules.wheezy old mode 100755 new mode 100644 diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj index efae3e02b..67a4fd452 100755 --- a/ext/installfiles/mac/ZeroTier One.pkgproj +++ b/ext/installfiles/mac/ZeroTier One.pkgproj @@ -701,7 +701,7 @@ USE_HFS+_COMPRESSION VERSION - 1.12.0 + 1.12.1 TYPE 0 diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip index 08ac17ecf..cd016670a 100644 --- a/ext/installfiles/windows/ZeroTier One.aip +++ b/ext/installfiles/windows/ZeroTier One.aip @@ -24,10 +24,10 @@ - + - + @@ -62,7 +62,7 @@ - + @@ -498,7 +498,7 @@ - + diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp index fe08f5195..614c6e725 100644 --- a/osdep/Binder.hpp +++ b/osdep/Binder.hpp @@ -87,11 +87,10 @@ namespace ZeroTier { class Binder { private: struct _Binding { - _Binding() : udpSock((PhySocket*)0), tcpListenSock((PhySocket*)0) + _Binding() : udpSock((PhySocket*)0) { } PhySocket* udpSock; - PhySocket* tcpListenSock; InetAddress address; char ifname[256] = {}; }; @@ -111,7 +110,6 @@ class Binder { Mutex::Lock _l(_lock); for (unsigned int b = 0, c = _bindingCount; b < c; ++b) { phy.close(_bindings[b].udpSock, false); - phy.close(_bindings[b].tcpListenSock, false); } _bindingCount = 0; } @@ -133,7 +131,7 @@ class Binder { template void refresh(Phy& phy, unsigned int* ports, unsigned int portCount, const std::vector explicitBind, INTERFACE_CHECKER& ifChecker) { std::map localIfAddrs; - PhySocket *udps, *tcps; + PhySocket *udps; Mutex::Lock _l(_lock); bool interfacesEnumerated = true; @@ -419,11 +417,8 @@ class Binder { } else { PhySocket* const udps = _bindings[b].udpSock; - PhySocket* const tcps = _bindings[b].tcpListenSock; _bindings[b].udpSock = (PhySocket*)0; - _bindings[b].tcpListenSock = (PhySocket*)0; phy.close(udps, false); - phy.close(tcps, false); } } @@ -437,24 +432,20 @@ class Binder { } if (bi == _bindingCount) { udps = phy.udpBind(reinterpret_cast(&(ii->first)), (void*)0, ZT_UDP_DESIRED_BUF_SIZE); - tcps = phy.tcpListen(reinterpret_cast(&(ii->first)), (void*)0); - if ((udps) && (tcps)) { + if (udps) { #ifdef __LINUX__ // Bind Linux sockets to their device so routes that we manage do not override physical routes (wish all platforms had this!) if (ii->second.length() > 0) { char tmp[256]; Utils::scopy(tmp, sizeof(tmp), ii->second.c_str()); int fd = (int)Phy::getDescriptor(udps); - if (fd >= 0) - setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, tmp, strlen(tmp)); - fd = (int)Phy::getDescriptor(tcps); - if (fd >= 0) + if (fd >= 0) { setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, tmp, strlen(tmp)); + } } #endif // __LINUX__ if (_bindingCount < ZT_BINDER_MAX_BINDINGS) { _bindings[_bindingCount].udpSock = udps; - _bindings[_bindingCount].tcpListenSock = tcps; _bindings[_bindingCount].address = ii->first; memcpy(_bindings[_bindingCount].ifname, (char*)ii->second.c_str(), (int)ii->second.length()); ++_bindingCount; @@ -462,7 +453,6 @@ class Binder { } else { phy.close(udps, false); - phy.close(tcps, false); } } } diff --git a/service/OneService.cpp b/service/OneService.cpp index b5e5c1924..dfa5df056 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -786,8 +786,11 @@ class OneServiceImpl : public OneService bool _updateAutoApply; httplib::Server _controlPlane; + httplib::Server _controlPlaneV6; std::thread _serverThread; + std::thread _serverThreadV6; bool _serverThreadRunning; + bool _serverThreadRunningV6; bool _allowTcpFallbackRelay; bool _forceTcpRelay; @@ -888,8 +891,11 @@ class OneServiceImpl : public OneService ,_updater((SoftwareUpdater *)0) ,_updateAutoApply(false) ,_controlPlane() + ,_controlPlaneV6() ,_serverThread() + ,_serverThreadV6() ,_serverThreadRunning(false) + ,_serverThreadRunningV6(false) ,_forceTcpRelay(false) ,_primaryPort(port) ,_udpPortPickerCounter(0) @@ -944,6 +950,10 @@ class OneServiceImpl : public OneService if (_serverThreadRunning) { _serverThread.join(); } + _controlPlaneV6.stop(); + if (_serverThreadRunningV6) { + _serverThreadV6.join(); + } #ifdef ZT_USE_MINIUPNPC delete _portMapper; @@ -1527,6 +1537,22 @@ class OneServiceImpl : public OneService // Internal HTTP Control Plane void startHTTPControlPlane() { + // control plane endpoints + std::string bondShowPath = "/bond/show/([0-9a-fA-F]{10})"; + std::string bondRotatePath = "/bond/rotate/([0-9a-fA-F]{10})"; + std::string setBondMtuPath = "/bond/setmtu/([0-9]{3,5})/([a-zA-Z0-9_]{1,16})/([0-9a-fA-F\\.\\:]{1,39})"; + std::string configPath = "/config"; + std::string configPostPath = "/config/settings"; + std::string healthPath = "/health"; + std::string moonListPath = "/moon"; + std::string moonPath = "/moon/([0-9a-fA-F]{10})"; + std::string networkListPath = "/network"; + std::string networkPath = "/network/([0-9a-fA-F]{16})"; + std::string peerListPath = "/peer"; + std::string peerPath = "/peer/([0-9a-fA-F]{10})"; + std::string statusPath = "/status"; + std::string metricsPath = "/metrics"; + std::vector noAuthEndpoints { "/sso", "/health" }; auto setContent = [=] (const httplib::Request &req, httplib::Response &res, std::string content) { @@ -1625,8 +1651,7 @@ class OneServiceImpl : public OneService }; - - _controlPlane.Get("/bond/show/([0-9a-fA-F]{10})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto bondShow = [&, setContent](const httplib::Request &req, httplib::Response &res) { if (!_node->bondController()->inUse()) { setContent(req, res, ""); res.status = 400; @@ -1652,7 +1677,9 @@ class OneServiceImpl : public OneService } } _node->freeQueryResult((void *)pl); - }); + }; + _controlPlane.Get(bondShowPath, bondShow); + _controlPlaneV6.Get(bondShowPath, bondShow); auto bondRotate = [&, setContent](const httplib::Request &req, httplib::Response &res) { if (!_node->bondController()->inUse()) { @@ -1675,8 +1702,10 @@ class OneServiceImpl : public OneService } setContent(req, res, "{}"); }; - _controlPlane.Post("/bond/rotate/([0-9a-fA-F]{10})", bondRotate); - _controlPlane.Put("/bond/rotate/([0-9a-fA-F]{10})", bondRotate); + _controlPlane.Post(bondRotatePath, bondRotate); + _controlPlane.Put(bondRotatePath, bondRotate); + _controlPlaneV6.Post(bondRotatePath, bondRotate); + _controlPlaneV6.Put(bondRotatePath, bondRotate); auto setMtu = [&, setContent](const httplib::Request &req, httplib::Response &res) { if (!_node->bondController()->inUse()) { @@ -1688,10 +1717,12 @@ class OneServiceImpl : public OneService res.status = _node->bondController()->setAllMtuByTuple(mtu, req.matches[2].str().c_str(), req.matches[3].str().c_str()) ? 200 : 400; setContent(req, res, "{}"); }; - _controlPlane.Post("/bond/setmtu/([0-9]{3,5})/([a-zA-Z0-9_]{1,16})/([0-9a-fA-F\\.\\:]{1,39})", setMtu); - _controlPlane.Put("/bond/setmtu/([0-9]{3,5})/([a-zA-Z0-9_]{1,16})/([0-9a-fA-F\\.\\:]{1,39})", setMtu); + _controlPlane.Post(setBondMtuPath, setMtu); + _controlPlane.Put(setBondMtuPath, setMtu); + _controlPlaneV6.Post(setBondMtuPath, setMtu); + _controlPlaneV6.Put(setBondMtuPath, setMtu); - _controlPlane.Get("/config", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto getConfig = [&, setContent](const httplib::Request &req, httplib::Response &res) { std::string config; { Mutex::Lock lc(_localConfig_m); @@ -1701,7 +1732,9 @@ class OneServiceImpl : public OneService config = "{}"; } setContent(req, res, config); - }); + }; + _controlPlane.Get(configPath, getConfig); + _controlPlaneV6.Get(configPath, getConfig); auto configPost = [&, setContent](const httplib::Request &req, httplib::Response &res) { json j(OSUtils::jsonParse(req.body)); @@ -1718,10 +1751,12 @@ class OneServiceImpl : public OneService } setContent(req, res, "{}"); }; - _controlPlane.Post("/config/settings", configPost); - _controlPlane.Put("/config/settings", configPost); + _controlPlane.Post(configPostPath, configPost); + _controlPlane.Put(configPostPath, configPost); + _controlPlaneV6.Post(configPostPath, configPost); + _controlPlaneV6.Put(configPostPath, configPost); - _controlPlane.Get("/health", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto healthGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { json out = json::object(); char tmp[256]; @@ -1739,9 +1774,11 @@ class OneServiceImpl : public OneService out["clock"] = OSUtils::now(); setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(healthPath, healthGet); + _controlPlaneV6.Get(healthPath, healthGet); - _controlPlane.Get("/moon", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto moonListGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { std::vector moons(_node->moons()); auto out = json::array(); @@ -1751,9 +1788,11 @@ class OneServiceImpl : public OneService out.push_back(mj); } setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(moonListPath, moonListGet); + _controlPlaneV6.Get(moonListPath, moonListGet); - _controlPlane.Get("/moon/([0-9a-fA-F]{10})", [&, setContent](const httplib::Request &req, httplib::Response &res){ + auto moonGet = [&, setContent](const httplib::Request &req, httplib::Response &res){ std::vector moons(_node->moons()); auto input = req.matches[1]; auto out = json::object(); @@ -1765,7 +1804,9 @@ class OneServiceImpl : public OneService } } setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(moonPath, moonGet); + _controlPlaneV6.Get(moonPath, moonGet); auto moonPost = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto input = req.matches[1]; @@ -1804,19 +1845,22 @@ class OneServiceImpl : public OneService } setContent(req, res, out.dump()); }; - _controlPlane.Post("/moon/([0-9a-fA-F]{10})", moonPost); - _controlPlane.Put("/moon/([0-9a-fA-F]{10})", moonPost); + _controlPlane.Post(moonPath, moonPost); + _controlPlane.Put(moonPath, moonPost); + _controlPlaneV6.Post(moonPath, moonPost); + _controlPlaneV6.Put(moonPath, moonPost); - _controlPlane.Delete("/moon/([0-9a-fA-F]{10})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto moonDelete = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto input = req.matches[1]; uint64_t id = Utils::hexStrToU64(input.str().c_str()); auto out = json::object(); _node->deorbit((void*)0,id); out["result"] = true; setContent(req, res, out.dump()); - }); + }; + _controlPlane.Delete(moonPath, moonDelete); - _controlPlane.Get("/network", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto networkListGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { Mutex::Lock _l(_nets_m); auto out = json::array(); @@ -1827,9 +1871,11 @@ class OneServiceImpl : public OneService out.push_back(nj); } setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(networkListPath, networkListGet); + _controlPlaneV6.Get(networkListPath, networkListGet); - _controlPlane.Get("/network/([0-9a-fA-F]{16})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto networkGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { Mutex::Lock _l(_nets_m); auto input = req.matches[1]; @@ -1843,7 +1889,9 @@ class OneServiceImpl : public OneService } setContent(req, res, ""); res.status = 404; - }); + }; + _controlPlane.Get(networkPath, networkGet); + _controlPlaneV6.Get(networkPath, networkGet); auto networkPost = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto input = req.matches[1]; @@ -1897,10 +1945,12 @@ class OneServiceImpl : public OneService setContent(req, res, out.dump()); #endif }; - _controlPlane.Post("/network/([0-9a-fA-F]{16})", networkPost); - _controlPlane.Put("/network/([0-9a-fA-F]){16}", networkPost); + _controlPlane.Post(networkPath, networkPost); + _controlPlane.Put(networkPath, networkPost); + _controlPlaneV6.Post(networkPath, networkPost); + _controlPlaneV6.Put(networkPath, networkPost); - _controlPlane.Delete("/network/([0-9a-fA-F]{16})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto networkDelete = [&, setContent](const httplib::Request &req, httplib::Response &res) { auto input = req.matches[1]; auto out = json::object(); ZT_VirtualNetworkList *nws = _node->networks(); @@ -1913,9 +1963,11 @@ class OneServiceImpl : public OneService } _node->freeQueryResult((void*)nws); setContent(req, res, out.dump()); - }); + }; + _controlPlane.Delete(networkPath, networkDelete); + _controlPlaneV6.Delete(networkPath, networkDelete); - _controlPlane.Get("/peer", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto peerListGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { ZT_PeerList *pl = _node->peers(); auto out = nlohmann::json::array(); @@ -1931,9 +1983,11 @@ class OneServiceImpl : public OneService } _node->freeQueryResult((void*)pl); setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(peerListPath, peerListGet); + _controlPlaneV6.Get(peerListPath, peerListGet); - _controlPlane.Get("/peer/([0-9a-fA-F]{10})", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto peerGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { ZT_PeerList *pl = _node->peers(); auto input = req.matches[1]; @@ -1951,9 +2005,11 @@ class OneServiceImpl : public OneService } _node->freeQueryResult((void*)pl); setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(peerPath, peerGet); + _controlPlaneV6.Get(peerPath, peerGet); - _controlPlane.Get("/status", [&, setContent](const httplib::Request &req, httplib::Response &res) { + auto statusGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { ZT_NodeStatus status; _node->status(&status); @@ -2016,10 +2072,13 @@ class OneServiceImpl : public OneService out["planetWorldTimestamp"] = planet.timestamp(); setContent(req, res, out.dump()); - }); + }; + _controlPlane.Get(statusPath, statusGet); + _controlPlaneV6.Get(statusPath, statusGet); #if ZT_SSO_ENABLED - _controlPlane.Get("/sso", [this](const httplib::Request &req, httplib::Response &res) { + std::string ssoPath = "/sso"; + auto ssoGet = [this](const httplib::Request &req, httplib::Response &res) { std::string htmlTemplatePath = _homePath + ZT_PATH_SEPARATOR + "sso-auth.template.html"; std::string htmlTemplate; if (!OSUtils::readFile(htmlTemplatePath.c_str(), htmlTemplate)) { @@ -2085,10 +2144,11 @@ class OneServiceImpl : public OneService zeroidc::free_cstr(ret); } - }); + }; + _controlPlane.Get(ssoPath, ssoGet); + _controlPlaneV6.Get(ssoPath, ssoGet); #endif - - _controlPlane.Get("/metrics", [this](const httplib::Request &req, httplib::Response &res) { + auto metricsGet = [this](const httplib::Request &req, httplib::Response &res) { std::string statspath = _homePath + ZT_PATH_SEPARATOR + "metrics.prom"; std::string metrics; if (OSUtils::readFile(statspath.c_str(), metrics)) { @@ -2097,9 +2157,11 @@ class OneServiceImpl : public OneService res.set_content("{}", "application/json"); res.status = 500; } - }); + }; + _controlPlane.Get(metricsPath, metricsGet); + _controlPlaneV6.Get(metricsPath, metricsGet); - _controlPlane.set_exception_handler([&, setContent](const httplib::Request &req, httplib::Response &res, std::exception_ptr ep) { + auto exceptionHandler = [&, setContent](const httplib::Request &req, httplib::Response &res, std::exception_ptr ep) { char buf[1024]; auto fmt = "{\"error\": %d, \"description\": \"%s\"}"; try { @@ -2111,39 +2173,70 @@ class OneServiceImpl : public OneService } setContent(req, res, buf); res.status = 500; - }); + }; + _controlPlane.set_exception_handler(exceptionHandler); + _controlPlaneV6.set_exception_handler(exceptionHandler); if (_controller) { - _controller->configureHTTPControlPlane(_controlPlane, setContent); + _controller->configureHTTPControlPlane(_controlPlane, _controlPlaneV6, setContent); } _controlPlane.set_pre_routing_handler(authCheck); + _controlPlaneV6.set_pre_routing_handler(authCheck); #if ZT_DEBUG==1 _controlPlane.set_logger([](const httplib::Request &req, const httplib::Response &res) { fprintf(stderr, "%s", http_log(req, res).c_str()); }); + _controlPlaneV6.set_logger([](const httplib::Request &req, const httplib::Response &res) { + fprintf(stderr, "%s", http_log(req, res).c_str()); + }); #endif if (_primaryPort==0) { fprintf(stderr, "unable to determine local control port"); exit(-1); } - if(!_controlPlane.bind_to_port("0.0.0.0", _primaryPort)) { - fprintf(stderr, "Error binding control plane to port %d\n", _primaryPort); - exit(-1); + bool v4controlPlaneBound = false; + _controlPlane.set_address_family(AF_INET); + if(_controlPlane.bind_to_port("0.0.0.0", _primaryPort)) { + _serverThread = std::thread([&] { + _serverThreadRunning = true; + fprintf(stderr, "Starting Control Plane...\n"); + if(!_controlPlane.listen_after_bind()) { + fprintf(stderr, "Error on listen_after_bind()\n"); + } + fprintf(stderr, "Control Plane Stopped\n"); + _serverThreadRunning = false; + }); + v4controlPlaneBound = true; + } else { + fprintf(stderr, "Error binding control plane to 0.0.0.0:%d\n", _primaryPort); + v4controlPlaneBound = false; + } + + bool v6controlPlaneBound = false; + _controlPlaneV6.set_address_family(AF_INET6); + if(_controlPlaneV6.bind_to_port("::", _primaryPort)) { + _serverThreadV6 = std::thread([&] { + _serverThreadRunningV6 = true; + fprintf(stderr, "Starting V6 Control Plane...\n"); + if(!_controlPlaneV6.listen_after_bind()) { + fprintf(stderr, "Error on V6 listen_after_bind()\n"); + } + fprintf(stderr, "V6 Control Plane Stopped\n"); + _serverThreadRunningV6 = false; + }); + v6controlPlaneBound = true; + } else { + fprintf(stderr, "Error binding control plane to [::]:%d\n", _primaryPort); + v6controlPlaneBound = false; } - _serverThread = std::thread([&] { - _serverThreadRunning = true; - fprintf(stderr, "Starting Control Plane...\n"); - if(!_controlPlane.listen_after_bind()) { - fprintf(stderr, "Error on listen_after_bind()\n"); - } - fprintf(stderr, "Control Plane Stopped\n"); - _serverThreadRunning = false; - }); - + if (!v4controlPlaneBound && !v6controlPlaneBound) { + fprintf(stderr, "ERROR: Could not bind control plane. Exiting...\n"); + exit(-1); + } } // Must be called after _localConfig is read or modified diff --git a/version.h b/version.h index cd4d29061..8c3a6c879 100644 --- a/version.h +++ b/version.h @@ -27,7 +27,7 @@ /** * Revision */ -#define ZEROTIER_ONE_VERSION_REVISION 0 +#define ZEROTIER_ONE_VERSION_REVISION 1 /** * Build version diff --git a/zerotier-one.spec b/zerotier-one.spec index dc9547003..a6ae2faf7 100644 --- a/zerotier-one.spec +++ b/zerotier-one.spec @@ -1,5 +1,5 @@ Name: zerotier-one -Version: 1.12.0 +Version: 1.12.1 Release: 1%{?dist} Summary: ZeroTier network virtualization service @@ -143,6 +143,9 @@ chmod 0755 $RPM_BUILD_ROOT/etc/init.d/zerotier-one %endif %changelog +* Fri Aug 25 2023 Adam Ierymenko - 1.12.1 +- see https://github.com/zerotier/ZeroTierOne for release notes + * Thu Aug 17 2023 Adam Ierymenko - 1.12.0 - see https://github.com/zerotier/ZeroTierOne for release notes