From b1c22949c568e3ae7539412e3a69051d0fd0a9e2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 3 Sep 2019 14:44:13 -0700 Subject: [PATCH] Cleanup, add support for distributing a new planet file --- node/Packet.hpp | 9 ++- root/root.cpp | 181 +++++++++++++++++++++++++++++------------------- 2 files changed, 116 insertions(+), 74 deletions(-) diff --git a/node/Packet.hpp b/node/Packet.hpp index bb20ac637..dc5ed0e7c 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -458,6 +458,9 @@ public: * <[8] in-re packet ID> * <[1] error code> * <[...] error-dependent payload> + * + * If this is not in response to a single packet then verb can be + * NOP and packet ID can be zero. */ VERB_ERROR = 0x02, @@ -962,7 +965,11 @@ public: ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */ /* Multicasts to this group are not wanted */ - ERROR_UNWANTED_MULTICAST = 0x08 + ERROR_UNWANTED_MULTICAST = 0x08, + + /* Cannot deliver a forwarded ZeroTier packet (e.g. hops exceeded, no routes) */ + /* Payload: , , <... additional packet ID / destinations> */ + ERROR_CANNOT_DELIVER = 0x09 }; template diff --git a/root/root.cpp b/root/root.cpp index c2946ee32..9e261fe1c 100644 --- a/root/root.cpp +++ b/root/root.cpp @@ -24,6 +24,7 @@ * "port": UDP port (int) * "httpPort": Local HTTP port for basic stats (int) * "relayMaxHops": Max hops (up to 7) + * "planetFile": Location of planet file for pre-2.x peers (string) * "statsRoot": If present, path to periodically save stats files (string) * "siblings": [ * { @@ -143,7 +144,7 @@ struct RendezvousKey */ struct RootPeer { - ZT_ALWAYS_INLINE RootPeer() : lastSend(0),lastReceive(0),lastSync(0),lastEcho(0),lastHello(0),vMajor(-1),vMinor(-1),vRev(-1),sibling(false) {} + ZT_ALWAYS_INLINE RootPeer() : lastSend(0),lastReceive(0),lastSync(0),lastEcho(0),lastHello(0),vProto(-1),vMajor(-1),vMinor(-1),vRev(-1),sibling(false) {} ZT_ALWAYS_INLINE ~RootPeer() { Utils::burn(key,sizeof(key)); } Identity id; // Identity @@ -154,6 +155,7 @@ struct RootPeer int64_t lastSync; // Time of last data synchronization with LF or other root state backend (currently unused) int64_t lastEcho; // Time of last received ECHO int64_t lastHello; // Time of last received HELLO + int vProto; // Protocol version int vMajor,vMinor,vRev; // Peer version or -1,-1,-1 if unknown bool sibling; // If true, this is a sibling root that will get forwards we don't know where to send std::mutex lock; @@ -161,13 +163,14 @@ struct RootPeer AtomicCounter __refCount; }; -static int64_t startTime; -static std::vector ports; -static int relayMaxHops = 0; -static Identity self; -static std::atomic_bool run; -static json config; -static std::string statsRoot; +static int64_t s_startTime; // Time service was started +static std::vector s_ports; // Ports to bind for UDP traffic +static int s_relayMaxHops = 0; // Max relay hops +static Identity s_self; // My identity (including secret) +static std::atomic_bool s_run; // Remains true until shutdown is ordered +static json s_config; // JSON config file contents +static std::string s_statsRoot; // Root to write stats, peers, etc. +static std::string s_planet; // Planet file contents to distribute with OK(HELLO) if any static Meter inputRate; static Meter outputRate; @@ -182,6 +185,7 @@ static std::unordered_map< Address,std::set< SharedPtr >,AddressHasher static std::unordered_map< InetAddress,std::set< SharedPtr >,InetAddressHasher > peersByPhysAddr; static std::unordered_map< RendezvousKey,int64_t,RendezvousKey::Hasher > lastRendezvous; +static std::mutex s_planet_l; static std::mutex siblings_l; static std::mutex multicastSubscriptions_l; static std::mutex peersByIdentity_l; @@ -202,7 +206,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip inputRate.log(now,pkt.size()); - if ((!fragment)&&(!pkt.fragmented())&&(dest == self.address())) { + if ((!fragment)&&(!pkt.fragmented())&&(dest == s_self.address())) { SharedPtr peer; // If this is an un-encrypted HELLO, either learn a new peer or verify @@ -227,7 +231,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip } } else { peer.set(new RootPeer); - if (self.agree(id,peer->key)) { + if (s_self.agree(id,peer->key)) { if (pkt.dearmor(peer->key)) { if (!pkt.uncompress()) { printf("%s HELLO rejected: decompression failed" ZT_EOL_S,ip->toString(ipstr)); @@ -297,13 +301,14 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip try { if ((now - peer->lastHello) > 1000) { peer->lastHello = now; + peer->vProto = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; peer->vMajor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; peer->vMinor = (int)pkt[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; peer->vRev = (int)pkt.template at(ZT_PROTO_VERB_HELLO_IDX_REVISION); const uint64_t origId = pkt.packetId(); const uint64_t ts = pkt.template at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); - pkt.reset(source,self.address(),Packet::VERB_OK); + pkt.reset(source,s_self.address(),Packet::VERB_OK); pkt.append((uint8_t)Packet::VERB_HELLO); pkt.append(origId); pkt.append(ts); @@ -312,6 +317,13 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip pkt.append((uint8_t)0); pkt.append((uint16_t)0); ip->serialize(pkt); + if (peer->vProto < 11) { // send planet file for pre-2.x peers + std::lock_guard pl(s_planet_l); + if (s_planet.length() > 0) { + pkt.append((uint16_t)s_planet.size()); + pkt.append((const uint8_t *)s_planet.data(),s_planet.size()); + } + } pkt.armor(peer->key,true); sendto(ip->isV4() ? v4s : v6s,pkt.data(),pkt.size(),SENDTO_FLAGS,(const struct sockaddr *)ip,(socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))); @@ -328,7 +340,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip if ((now - peer->lastEcho) > 1000) { peer->lastEcho = now; - Packet outp(source,self.address(),Packet::VERB_OK); + Packet outp(source,s_self.address(),Packet::VERB_OK); outp.append((uint8_t)Packet::VERB_ECHO); outp.append(pkt.packetId()); outp.append(((const uint8_t *)pkt.data()) + ZT_PACKET_IDX_PAYLOAD,pkt.size() - ZT_PACKET_IDX_PAYLOAD); @@ -359,7 +371,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip if (!results.empty()) { const uint64_t origId = pkt.packetId(); - pkt.reset(source,self.address(),Packet::VERB_OK); + pkt.reset(source,s_self.address(),Packet::VERB_OK); pkt.append((uint8_t)Packet::VERB_WHOIS); pkt.append(origId); for(auto p=results.begin();p!=results.end();++p) @@ -398,7 +410,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip gatherLimit = 255; const uint64_t origId = pkt.packetId(); - pkt.reset(source,self.address(),Packet::VERB_OK); + pkt.reset(source,s_self.address(),Packet::VERB_OK); pkt.append((uint8_t)Packet::VERB_MULTICAST_GATHER); pkt.append(origId); pkt.append(nwid); @@ -451,26 +463,29 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip // If we made it here, we are forwarding this packet to someone else and also possibly // sending a RENDEZVOUS message. + int hops = 0; bool introduce = false; if (fragment) { - if ((int)reinterpret_cast(&pkt)->incrementHops() > relayMaxHops) { + if ((hops = (int)reinterpret_cast(&pkt)->incrementHops()) > s_relayMaxHops) { //printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); discardedForwardRate.log(now,pkt.size()); return; } } else { - if ((int)pkt.incrementHops() > relayMaxHops) { + if ((hops = (int)pkt.incrementHops()) > s_relayMaxHops) { //printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); discardedForwardRate.log(now,pkt.size()); return; } - RendezvousKey rk(source,dest); - std::lock_guard l(lastRendezvous_l); - int64_t &lr = lastRendezvous[rk]; - if ((now - lr) >= 45000) { - lr = now; - introduce = true; + if (hops == 1) { + RendezvousKey rk(source,dest); + std::lock_guard l(lastRendezvous_l); + int64_t &lr = lastRendezvous[rk]; + if ((now - lr) >= 45000) { + lr = now; + introduce = true; + } } } @@ -515,7 +530,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip //printf("* introducing %s(%s) to %s(%s)" ZT_EOL_S,ip->toString(ipstr),source.toString(astr),b->second->ip6.toString(ipstr2),dest.toString(astr2)); // Introduce source to destination (V6) - Packet outp(source,self.address(),Packet::VERB_RENDEZVOUS); + Packet outp(source,s_self.address(),Packet::VERB_RENDEZVOUS); outp.append((uint8_t)0); dest.appendTo(outp); outp.append((uint16_t)b->second->ip6.port()); @@ -528,7 +543,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip (*a)->lastSend = now; // Introduce destination to source (V6) - outp.reset(dest,self.address(),Packet::VERB_RENDEZVOUS); + outp.reset(dest,s_self.address(),Packet::VERB_RENDEZVOUS); outp.append((uint8_t)0); source.appendTo(outp); outp.append((uint16_t)ip->port()); @@ -544,7 +559,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip //printf("* introducing %s(%s) to %s(%s)" ZT_EOL_S,ip->toString(ipstr),source.toString(astr),b->second->ip4.toString(ipstr2),dest.toString(astr2)); // Introduce source to destination (V4) - Packet outp(source,self.address(),Packet::VERB_RENDEZVOUS); + Packet outp(source,s_self.address(),Packet::VERB_RENDEZVOUS); outp.append((uint8_t)0); dest.appendTo(outp); outp.append((uint16_t)b->second->ip4.port()); @@ -557,7 +572,7 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip (*a)->lastSend = now; // Introduce destination to source (V4) - outp.reset(dest,self.address(),Packet::VERB_RENDEZVOUS); + outp.reset(dest,s_self.address(),Packet::VERB_RENDEZVOUS); outp.append((uint8_t)0); source.appendTo(outp); outp.append((uint16_t)ip->port()); @@ -648,7 +663,7 @@ static int bindSocket(struct sockaddr *const bindAddr) return s; } -static void shutdownSigHandler(int sig) { run = false; } +static void shutdownSigHandler(int sig) { s_run = false; } int main(int argc,char **argv) { @@ -660,7 +675,7 @@ int main(int argc,char **argv) signal(SIGUSR2,SIG_IGN); signal(SIGCHLD,SIG_IGN); - startTime = OSUtils::now(); + s_startTime = OSUtils::now(); if (argc < 3) { printf("Usage: zerotier-root " ZT_EOL_S); @@ -673,11 +688,11 @@ int main(int argc,char **argv) printf("FATAL: cannot read identity.secret at %s" ZT_EOL_S,argv[1]); return 1; } - if (!self.fromString(myIdStr.c_str())) { + if (!s_self.fromString(myIdStr.c_str())) { printf("FATAL: cannot read identity.secret at %s (invalid identity)" ZT_EOL_S,argv[1]); return 1; } - if (!self.hasPrivate()) { + if (!s_self.hasPrivate()) { printf("FATAL: cannot read identity.secret at %s (missing secret key)" ZT_EOL_S,argv[1]); return 1; } @@ -689,7 +704,7 @@ int main(int argc,char **argv) return 1; } try { - config = json::parse(configStr); + s_config = json::parse(configStr); } catch (std::exception &exc) { printf("FATAL: config file at %s invalid: %s" ZT_EOL_S,argv[2],exc.what()); return 1; @@ -697,14 +712,14 @@ int main(int argc,char **argv) printf("FATAL: config file at %s invalid: unknown exception" ZT_EOL_S,argv[2]); return 1; } - if (!config.is_object()) { + if (!s_config.is_object()) { printf("FATAL: config file at %s invalid: does not contain a JSON object" ZT_EOL_S,argv[2]); return 1; } } try { - auto jport = config["port"]; + auto jport = s_config["port"]; if (jport.is_array()) { for(long i=0;i<(long)jport.size();++i) { int port = jport[i]; @@ -712,7 +727,7 @@ int main(int argc,char **argv) printf("FATAL: invalid port in config file %d" ZT_EOL_S,port); return 1; } - ports.push_back(port); + s_ports.push_back(port); } } else { int port = jport; @@ -720,16 +735,16 @@ int main(int argc,char **argv) printf("FATAL: invalid port in config file %d" ZT_EOL_S,port); return 1; } - ports.push_back(port); + s_ports.push_back(port); } } catch ( ... ) {} - if (ports.empty()) - ports.push_back(ZT_DEFAULT_PORT); - std::sort(ports.begin(),ports.end()); + if (s_ports.empty()) + s_ports.push_back(ZT_DEFAULT_PORT); + std::sort(s_ports.begin(),s_ports.end()); int httpPort = ZT_DEFAULT_PORT; try { - httpPort = config["httpPort"]; + httpPort = s_config["httpPort"]; if ((httpPort <= 0)||(httpPort > 65535)) { printf("FATAL: invalid HTTP port in config file %d" ZT_EOL_S,httpPort); return 1; @@ -738,29 +753,36 @@ int main(int argc,char **argv) httpPort = ZT_DEFAULT_PORT; } + std::string planetFilePath; try { - statsRoot = config["statsRoot"]; - while ((statsRoot.length() > 0)&&(statsRoot[statsRoot.length()-1] == ZT_PATH_SEPARATOR)) - statsRoot = statsRoot.substr(0,statsRoot.length()-1); - if (statsRoot.length() > 0) - OSUtils::mkdir(statsRoot); + planetFilePath = s_config["planetFile"]; } catch ( ... ) { - statsRoot = ""; - } - - relayMaxHops = ZT_RELAY_MAX_HOPS; - try { - relayMaxHops = config["relayMaxHops"]; - if (relayMaxHops > ZT_PROTO_MAX_HOPS) - relayMaxHops = ZT_PROTO_MAX_HOPS; - else if (relayMaxHops < 0) - relayMaxHops = 0; - } catch ( ... ) { - relayMaxHops = ZT_RELAY_MAX_HOPS; + planetFilePath = ""; } try { - auto sibs = config["siblings"]; + s_statsRoot = s_config["statsRoot"]; + while ((s_statsRoot.length() > 0)&&(s_statsRoot[s_statsRoot.length()-1] == ZT_PATH_SEPARATOR)) + s_statsRoot = s_statsRoot.substr(0,s_statsRoot.length()-1); + if (s_statsRoot.length() > 0) + OSUtils::mkdir(s_statsRoot); + } catch ( ... ) { + s_statsRoot = ""; + } + + s_relayMaxHops = ZT_RELAY_MAX_HOPS; + try { + s_relayMaxHops = s_config["s_relayMaxHops"]; + if (s_relayMaxHops > ZT_PROTO_MAX_HOPS) + s_relayMaxHops = ZT_PROTO_MAX_HOPS; + else if (s_relayMaxHops < 0) + s_relayMaxHops = 0; + } catch ( ... ) { + s_relayMaxHops = ZT_RELAY_MAX_HOPS; + } + + try { + auto sibs = s_config["siblings"]; if (sibs.is_array()) { for(long i=0;i<(long)sibs.size();++i) { auto sib = sibs[i]; @@ -780,7 +802,7 @@ int main(int argc,char **argv) ip.setPort((unsigned int)sib["port"]); SharedPtr rp(new RootPeer); rp->id = id; - if (!self.agree(id,rp->key)) { + if (!s_self.agree(id,rp->key)) { printf("FATAL: invalid JSON while parsing siblings section in config file: invalid identity in sibling entry (unable to execute key agreement)" ZT_EOL_S); return 1; } @@ -814,13 +836,13 @@ int main(int argc,char **argv) unsigned int ncores = std::thread::hardware_concurrency(); if (ncores == 0) ncores = 1; - run = true; + s_run = true; std::vector threads; std::vector sockets; int v4Sock = -1,v6Sock = -1; - for(auto port=ports.begin();port!=ports.end();++port) { + for(auto port=s_ports.begin();port!=s_ports.end();++port) { for(unsigned int tn=0;tnid.address(),self.address(),Packet::VERB_HELLO); + Packet outp((*s)->id.address(),s_self.address(),Packet::VERB_HELLO); outp.append((uint8_t)ZT_PROTO_VERSION); - outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR); - outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR); - outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + outp.append((uint8_t)0); + outp.append((uint8_t)0); + outp.append((uint16_t)0); outp.append((uint64_t)now); - self.serialize(outp,false); + s_self.serialize(outp,false); if ((*s)->ip4) { (*s)->ip4.serialize(outp); ip = &((*s)->ip4); @@ -1074,11 +1096,24 @@ int main(int argc,char **argv) } } - // Write stats if configured to do so - if (((now - lastWroteStats) > 15000)&&(statsRoot.length() > 0)) { + // Write stats if configured to do so, and periodically refresh planet file (if any) + if (((now - lastWroteStats) > 15000)&&(s_statsRoot.length() > 0)) { lastWroteStats = now; - std::string peersFilePath(statsRoot); + try { + if (planetFilePath.length() > 0) { + std::string planetData; + if ((OSUtils::readFile(planetFilePath.c_str(),planetData))&&(planetData.length() > 0)) { + std::lock_guard pl(s_planet_l); + s_planet = planetData; + } + } + } catch ( ... ) { + std::lock_guard pl(s_planet_l); + s_planet.clear(); + } + + std::string peersFilePath(s_statsRoot); peersFilePath.append("/.peers.tmp"); FILE *pf = fopen(peersFilePath.c_str(),"wb"); if (pf) { @@ -1110,17 +1145,17 @@ int main(int argc,char **argv) } fclose(pf); - std::string peersFilePath2(statsRoot); + std::string peersFilePath2(s_statsRoot); peersFilePath2.append("/peers"); OSUtils::rm(peersFilePath2); OSUtils::rename(peersFilePath.c_str(),peersFilePath2.c_str()); } - std::string statsFilePath(statsRoot); + std::string statsFilePath(s_statsRoot); statsFilePath.append("/.stats.tmp"); FILE *sf = fopen(statsFilePath.c_str(),"wb"); if (sf) { - fprintf(sf,"Uptime (seconds) : %ld" ZT_EOL_S,(long)((now - startTime) / 1000)); + fprintf(sf,"Uptime (seconds) : %ld" ZT_EOL_S,(long)((now - s_startTime) / 1000)); peersByIdentity_l.lock(); fprintf(sf,"Peers : %llu" ZT_EOL_S,(unsigned long long)peersByIdentity.size()); peersByVirtAddr_l.lock(); @@ -1140,7 +1175,7 @@ int main(int argc,char **argv) fprintf(sf,"Discarded Forward BPS : %.4f" ZT_EOL_S,discardedForwardRate.perSecond(now)); fclose(sf); - std::string statsFilePath2(statsRoot); + std::string statsFilePath2(s_statsRoot); statsFilePath2.append("/stats"); OSUtils::rm(statsFilePath2); OSUtils::rename(statsFilePath.c_str(),statsFilePath2.c_str());