diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 2ccc16b8c..95c9cc82b 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #ifndef _WIN32 #include @@ -553,6 +554,528 @@ void EmbeddedNetworkController::request( _queue.post(qe); } +void EmbeddedNetworkController::configureHTTPControlPlane( + httplib::Server &s, + const std::function setContent) +{ + s.Get("/controller/network", [&](const httplib::Request &req, httplib::Response &res) { + std::set networkIds; + _db.networks(networkIds); + char tmp[64]; + + auto out = json::array(); + for(std::set::const_iterator i(networkIds.begin()); i != networkIds.end(); ++i) { + OSUtils::ztsnprintf(tmp, sizeof(tmp), "%.16llx", *i); + out.push_back(tmp); + } + + setContent(req, res, out.dump()); + }); + + s.Get("/controller/network/([0-9a-fA-F]{16})", [&](const httplib::Request &req, httplib::Response &res) { + auto networkID = req.matches[1]; + uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); + json network; + if (!_db.get(nwid, network)) { + res.status = 404; + return; + } + + setContent(req, res, network.dump()); + }); + + auto _networkUpdateFromPostData = [&](uint64_t networkID, const std::string &body) -> std::string { + json b = OSUtils::jsonParse(body); + + char nwids[24]; + OSUtils::ztsnprintf(nwids, sizeof(nwids), "%.16llx", networkID); + + json network; + _db.get(networkID, network); + DB::initNetwork(network); + if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],""); + if (b.count("private")) network["private"] = OSUtils::jsonBool(b["private"],true); + if (b.count("enableBroadcast")) network["enableBroadcast"] = OSUtils::jsonBool(b["enableBroadcast"],false); + if (b.count("multicastLimit")) network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"],32ULL); + if (b.count("mtu")) network["mtu"] = std::max(std::min((unsigned int)OSUtils::jsonInt(b["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU); + + if (b.count("remoteTraceTarget")) { + const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],"")); + if (rtt.length() == 10) { + network["remoteTraceTarget"] = rtt; + } else { + network["remoteTraceTarget"] = json(); + } + } + if (b.count("remoteTraceLevel")) network["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL); + + if (b.count("v4AssignMode")) { + json nv4m; + json &v4m = b["v4AssignMode"]; + if (v4m.is_string()) { // backward compatibility + nv4m["zt"] = (OSUtils::jsonString(v4m,"") == "zt"); + } else if (v4m.is_object()) { + nv4m["zt"] = OSUtils::jsonBool(v4m["zt"],false); + } else nv4m["zt"] = false; + network["v4AssignMode"] = nv4m; + } + + if (b.count("v6AssignMode")) { + json nv6m; + json &v6m = b["v6AssignMode"]; + if (!nv6m.is_object()) nv6m = json::object(); + if (v6m.is_string()) { // backward compatibility + std::vector v6ms(OSUtils::split(OSUtils::jsonString(v6m,"").c_str(),",","","")); + std::sort(v6ms.begin(),v6ms.end()); + v6ms.erase(std::unique(v6ms.begin(),v6ms.end()),v6ms.end()); + nv6m["rfc4193"] = false; + nv6m["zt"] = false; + nv6m["6plane"] = false; + for(std::vector::iterator i(v6ms.begin());i!=v6ms.end();++i) { + if (*i == "rfc4193") + nv6m["rfc4193"] = true; + else if (*i == "zt") + nv6m["zt"] = true; + else if (*i == "6plane") + nv6m["6plane"] = true; + } + } else if (v6m.is_object()) { + if (v6m.count("rfc4193")) nv6m["rfc4193"] = OSUtils::jsonBool(v6m["rfc4193"],false); + if (v6m.count("zt")) nv6m["zt"] = OSUtils::jsonBool(v6m["zt"],false); + if (v6m.count("6plane")) nv6m["6plane"] = OSUtils::jsonBool(v6m["6plane"],false); + } else { + nv6m["rfc4193"] = false; + nv6m["zt"] = false; + nv6m["6plane"] = false; + } + network["v6AssignMode"] = nv6m; + } + + if (b.count("routes")) { + json &rts = b["routes"]; + if (rts.is_array()) { + json nrts = json::array(); + for(unsigned long i=0;i().c_str()); + InetAddress v; + if (via.is_string()) v.fromString(via.get().c_str()); + if ( ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) && (t.netmaskBitsValid()) ) { + json tmp; + char tmp2[64]; + tmp["target"] = t.toString(tmp2); + if (v.ss_family == t.ss_family) + tmp["via"] = v.toIpString(tmp2); + else tmp["via"] = json(); + nrts.push_back(tmp); + if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + } + network["routes"] = nrts; + } + } + + if (b.count("ipAssignmentPools")) { + json &ipp = b["ipAssignmentPools"]; + if (ipp.is_array()) { + json nipp = json::array(); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + network["ipAssignmentPools"] = nipp; + } + } + + if (b.count("rules")) { + json &rules = b["rules"]; + if (rules.is_array()) { + json nrules = json::array(); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + network["rules"] = nrules; + } + } + + if (b.count("authTokens")) { + json &authTokens = b["authTokens"]; + if (authTokens.is_object()) { + json nat; + for(json::iterator t(authTokens.begin());t!=authTokens.end();++t) { + if ((t.value().is_number())&&(t.value() >= 0)) + nat[t.key()] = t.value(); + } + network["authTokens"] = nat; + } else { + network["authTokens"] = {{}}; + } + } + + if (b.count("capabilities")) { + json &capabilities = b["capabilities"]; + if (capabilities.is_array()) { + std::map< uint64_t,json > ncaps; + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + } + ncap["rules"] = nrules; + + ncaps[capId] = ncap; + } + } + + json ncapsa = json::array(); + for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) { + ncapsa.push_back(c->second); + if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + network["capabilities"] = ncapsa; + } + } + + if (b.count("tags")) { + json &tags = b["tags"]; + if (tags.is_array()) { + std::map< uint64_t,json > ntags; + for(unsigned long i=0;i::iterator t(ntags.begin());t!=ntags.end();++t) { + ntagsa.push_back(t->second); + if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + network["tags"] = ntagsa; + } + } + + if (b.count("dns")) { + json &dns = b["dns"]; + if (dns.is_object()) { + json nd; + + nd["domain"] = dns["domain"]; + + json &srv = dns["servers"]; + if (srv.is_array()) { + json ns = json::array(); + for(unsigned int i=0;i memTmp; + if (_db.get(nwid, network, memTmp)) { + for (auto m = memTmp.begin(); m != memTmp.end(); ++m) { + int revision = OSUtils::jsonInt((*m)["revsision"], 0); + std::string id = OSUtils::jsonString((*m)["id"], ""); + if (id.length() == 10) { + json tmp = json::object(); + tmp[id] = revision; + out.push_back(tmp); + } + } + } + + setContent(req, res, out.dump()); + }); + + s.Get("/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})", [&](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()); + uint64_t memid = Utils::hexStrToU64(memberID.str().c_str()); + json network; + json member; + if (!_db.get(nwid, network, memid, member)) { + res.status = 404; + return; + } + + setContent(req, res, member.dump()); + }); + + auto memberPost = [&](const httplib::Request &req, httplib::Response &res) { + auto networkID = req.matches[1].str(); + auto memberID = req.matches[2].str(); + uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); + uint64_t memid = Utils::hexStrToU64(memberID.c_str()); + json network; + json member; + _db.get(nwid, network, memid, member); + DB::initMember(member); + + json b = OSUtils::jsonParse(req.body); + + if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"], false); + if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"], false); + if (b.count("authenticationExpiryTime")) member["authenticationExpiryTime"] = (uint64_t)OSUtils::jsonInt(b["authenticationExpiryTime"], 0ULL); + if (b.count("authenticationURL")) member["authenticationURL"] = OSUtils::jsonString(b["authenticationURL"], ""); + + if (b.count("remoteTraceTarget")) { + const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],"")); + if (rtt.length() == 10) { + member["remoteTraceTarget"] = rtt; + } else { + member["remoteTraceTarget"] = json(); + } + } + if (b.count("remoteTraceLevel")) member["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL); + + if (b.count("authorized")) { + const bool newAuth = OSUtils::jsonBool(b["authorized"],false); + if (newAuth != OSUtils::jsonBool(member["authorized"],false)) { + member["authorized"] = newAuth; + member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = OSUtils::now(); + if (newAuth) { + member["lastAuthorizedCredentialType"] = "api"; + member["lastAuthorizedCredential"] = json(); + } + } + } + + if (b.count("ipAssignments")) { + json &ipa = b["ipAssignments"]; + if (ipa.is_array()) { + json mipa(json::array()); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + member["ipAssignments"] = mipa; + } + } + + if (b.count("tags")) { + json &tags = b["tags"]; + if (tags.is_array()) { + std::map mtags; + for(unsigned long i=0;i::iterator t(mtags.begin());t!=mtags.end();++t) { + json ta = json::array(); + ta.push_back(t->first); + ta.push_back(t->second); + mtagsa.push_back(ta); + if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + member["tags"] = mtagsa; + } + } + + if (b.count("capabilities")) { + json &capabilities = b["capabilities"]; + if (capabilities.is_array()) { + json mcaps = json::array(); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + std::sort(mcaps.begin(),mcaps.end()); + mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end()); + member["capabilities"] = mcaps; + } + } + + member["id"] = memberID; + member["address"] = memberID; + member["nwid"] = networkID; + + DB::cleanMember(member); + _db.save(member, true); + + 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.Delete("/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})", [&](const httplib::Request &req, httplib::Response &res) { + auto networkID = req.matches[1].str(); + auto memberID = req.matches[2].str(); + + uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); + uint64_t address = Utils::hexStrToU64(memberID.c_str()); + json network, member; + + if (!_db.get(nwid, network, address, member)) { + res.status = 404; + return; + } + + if (!member.size()) { + res.status = 404; + return; + } + + _db.eraseMember(nwid, address); + + setContent(req, res, member.dump()); + }); +} + unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( const std::vector &path, const std::map &urlArgs, diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index bc95acb58..7269a83cc 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -37,6 +37,8 @@ #include +#include + #include "DB.hpp" #include "DBMirrorSet.hpp" @@ -66,6 +68,10 @@ public: const Identity &identity, const Dictionary &metaData); + void configureHTTPControlPlane( + httplib::Server &s, + const std::function); + unsigned int handleControlPlaneHttpGET( const std::vector &path, const std::map &urlArgs, diff --git a/service/OneService.cpp b/service/OneService.cpp index 99007e0c5..b6d33fec5 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1985,6 +1985,7 @@ public: if (_controller) { // TODO: Wire up controller + _controller->configureHTTPControlPlane(_controlPlane, setContent); } _controlPlane.set_pre_routing_handler(authCheck);