diff --git a/node/Identity.hpp b/node/Identity.hpp index d927c0087..6d242a257 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -428,7 +428,7 @@ public: ZT_ALWAYS_INLINE bool operator<=(const Identity &id) const { return !(id < *this); } ZT_ALWAYS_INLINE bool operator>=(const Identity &id) const { return !(*this < id); } - ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_address.toInt(); } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)(_address.toInt() << 8) + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); } private: Address _address; diff --git a/node/Packet.hpp b/node/Packet.hpp index ae8780627..b5877496b 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -60,7 +60,7 @@ * 11 - 2.0.0 ... CURRENT * + Peer-to-peer multicast replication (optional) * + Old planet/moon stuff is DEAD! - * + AES256-GCM encryption is now the default + * + AES-256-GMAC-CTR encryption is now the default * + NIST P-384 (type 1) identities now supported * + Minimum proto version is now 8 (1.1.17 and newer) * + WILL_RELAY allows mesh-like operation @@ -105,9 +105,9 @@ #define ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH 2 /** - * AES256/GCM + * AES-256-GMAC-CTR */ -#define ZT_PROTO_CIPHER_SUITE__AES256_GCM 3 +#define ZT_PROTO_CIPHER_SUITE__AES256_GMAC_CTR 3 /** * Header flag indicating that a packet is fragmented @@ -395,9 +395,9 @@ public: /** * Increment this packet's hop count */ - ZT_ALWAYS_INLINE void incrementHops() + ZT_ALWAYS_INLINE unsigned int incrementHops() { - (*this)[ZT_PACKET_FRAGMENT_IDX_HOPS] = (((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]) + 1) & ZT_PROTO_MAX_HOPS; + return (unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS] = (((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]) + 1)); } /** @@ -1112,10 +1112,12 @@ public: /** * Increment this packet's hop count */ - ZT_ALWAYS_INLINE void incrementHops() + ZT_ALWAYS_INLINE unsigned char incrementHops() { unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS]; - b = (b & 0xf8) | ((b + 1) & 0x07); + const unsigned char h = (b + 1) & 0x07; + b = (b & 0xf8) | h; + return (unsigned int)h; } /** diff --git a/root/Makefile b/root/Makefile new file mode 100644 index 000000000..38e89797f --- /dev/null +++ b/root/Makefile @@ -0,0 +1,3 @@ +all: + rm -f zerotier-root + c++ -std=c++11 -O3 -maes -mpclmul -msse -msse2 -msse3 -msse4.1 -pthread -o zerotier-root root.cpp ../node/*.cpp ../osdep/OSUtils.cpp -lresolv diff --git a/root/root.cpp b/root/root.cpp index aa3ba4506..950bd73e1 100644 --- a/root/root.cpp +++ b/root/root.cpp @@ -51,38 +51,174 @@ #include "../node/Identity.hpp" #include "../node/InetAddress.hpp" #include "../node/Mutex.hpp" +#include "../node/SharedPtr.hpp" #include "../osdep/OSUtils.hpp" #include #include #include +#include #include #include #include #include #include +#include using namespace ZeroTier; struct PeerInfo { - InetAddress address; + Identity id; + uint8_t key[32]; + InetAddress ip4,ip6; int64_t lastReceive; + AtomicCounter __refCount; + + ZT_ALWAYS_INLINE ~PeerInfo() { Utils::burn(key,sizeof(key)); } }; +struct IdentityHasher { ZT_ALWAYS_INLINE std::size_t operator()(const Identity &id) const { return (std::size_t)id.hashCode(); } }; struct AddressHasher { ZT_ALWAYS_INLINE std::size_t operator()(const Address &a) const { return (std::size_t)a.toInt(); } }; struct InetAddressHasher { ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress &ip) const { return (std::size_t)ip.hashCode(); } }; +static Identity self; static std::atomic_bool run; -static std::unordered_map< Address,std::map< Identity,PeerInfo >,AddressHasher > peersByVirtAddr; -static std::unordered_map< InetAddress,std::map< Identity,PeerInfo >,InetAddressHasher > peersByPhysAddr; -static Mutex peersByVirtAddr_l; -static Mutex peersByAddress_l; +static std::unordered_map< Identity,SharedPtr,IdentityHasher > peersByIdentity; +static std::unordered_map< Address,std::set< SharedPtr >,AddressHasher > peersByVirtAddr; +static std::unordered_map< InetAddress,std::set< SharedPtr >,InetAddressHasher > peersByPhysAddr; +static std::mutex peersByIdentity_l; +static std::mutex peersByVirtAddr_l; +static std::mutex peersByPhysAddr_l; -static void handlePacket(const InetAddress *const ip,const Packet *const pkt) +static void handlePacket(const InetAddress *const ip,const Packet *const inpkt) { - char stmp[128]; - printf("%s\n",ip->toString(stmp)); + Packet pkt(*inpkt); + char ipstr[128],ipstr2[128],astr[32]; + const bool fragment = pkt[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR; + + // See if this is destined for us and isn't a fragment / fragmented. (No packets + // understood by the root are fragments/fragmented.) + if ((!fragment)&&(!pkt.fragmented())&&(pkt.destination() == self.address())) { + SharedPtr peer; + + // If this is an un-encrypted HELLO, either learn a new peer or verify + // that this is a peer we already know. + if ((pkt.cipher() == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE)&&(pkt.verb() == Packet::VERB_HELLO)) { + Identity id; + if (id.deserialize(pkt,ZT_PROTO_VERB_HELLO_IDX_IDENTITY)) { + { + std::lock_guard pbi_l(peersByIdentity_l); + auto pById = peersByIdentity.find(id); + if (pById != peersByIdentity.end()) { + peer = pById->second; + } + } + if (peer) { + if (!pkt.dearmor(peer->key)) { + printf("%s HELLO rejected: packet authentication failed" ZT_EOL_S,ip->toString(ipstr)); + return; + } + } else { + peer.set(new PeerInfo); + if (id.agree(self,peer->key)) { + if (pkt.dearmor(peer->key)) { + if (id.locallyValidate()) { + peer->id = id; + { + std::lock_guard pbi_l(peersByIdentity_l); + peersByIdentity.emplace(id,peer); + } + } else { + printf("%s HELLO rejected: invalid identity (locallyValidate() failed)" ZT_EOL_S,ip->toString(ipstr)); + return; + } + } else { + printf("%s HELLO rejected: packet authentication failed" ZT_EOL_S,ip->toString(ipstr)); + return; + } + } else { + printf("%s HELLO rejected: key agreement failed" ZT_EOL_S,ip->toString(ipstr)); + return; + } + } + } + } + + // If it wasn't a HELLO, check to see if any known identities for the sender's + // short ZT address successfully decrypt the packet. + if (!peer) { + std::lock_guard pbv_l(peersByVirtAddr_l); + auto peers = peersByVirtAddr.find(pkt.source()); + if (peers != peersByVirtAddr.end()) { + for(auto p=peers->second.begin();p!=peers->second.end();++p) { + if (pkt.dearmor((*p)->key)) { + peer = (*p); + break; + } else { + pkt = *inpkt; // dearmor() destroys contents of pkt + } + } + } + } + + // If we found the peer, update IP and/or time. + if (peer) { + InetAddress *const peerIp = (ip->ss_family == AF_INET) ? &(peer->ip4) : &(peer->ip6); + if (*peerIp != ip) { + std::lock_guard pbp_l(peersByPhysAddr_l); + if (*peerIp) { + auto prev = peersByPhysAddr.find(*peerIp); + if (prev != peersByPhysAddr.end()) { + prev->second.erase(peer); + if (prev->second.empty()) + peersByPhysAddr.erase(prev); + } + } + *peerIp = ip; + peersByPhysAddr[ip].emplace(peer); + } + + peer->lastReceive = OSUtils::now(); + + printf("%s has %s" ZT_EOL_S,ip->toString(ipstr),pkt.source().toString(astr)); + return; + } + } + + std::vector toAddrs; + { + std::lock_guard pbv_l(peersByVirtAddr_l); + auto peers = peersByVirtAddr.find(inpkt->destination()); + if (peers != peersByVirtAddr.end()) { + for(auto p=peers->second.begin();p!=peers->second.end();++p) { + if ((*p)->ip6) + toAddrs.push_back((*p)->ip6); + else if ((*p)->ip4) + toAddrs.push_back((*p)->ip4); + } + } + } + if (toAddrs.empty()) { + printf("%s not forwarding to %s: no destinations found" ZT_EOL_S,ip->toString(ipstr),pkt.destination().toString(astr)); + return; + } + + if (fragment) { + if (reinterpret_cast(&pkt)->incrementHops() >= ZT_PROTO_MAX_HOPS) { + printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),pkt.destination().toString(astr)); + return; + } + } else { + if (pkt.incrementHops() >= ZT_PROTO_MAX_HOPS) { + printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),pkt.destination().toString(astr)); + return; + } + } + + for(auto i=toAddrs.begin();i!=toAddrs.end();++i) { + printf("%s -> %s for %s" ZT_EOL_S,ip->toString(ipstr),i->toString(ipstr2),pkt.destination().toString(astr)); + } } static int bindSocket(struct sockaddr *bindAddr)