diff options
Diffstat (limited to 'node/Network.hpp')
-rw-r--r-- | node/Network.hpp | 348 |
1 files changed, 305 insertions, 43 deletions
diff --git a/node/Network.hpp b/node/Network.hpp index 417688f0..637c6664 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -30,33 +30,277 @@ #include <string> #include <set> +#include <map> #include <vector> #include <stdexcept> + +#include "Constants.hpp" +#include "Utils.hpp" #include "EthernetTap.hpp" #include "Address.hpp" #include "Mutex.hpp" -#include "InetAddress.hpp" -#include "Constants.hpp" #include "SharedPtr.hpp" #include "AtomicCounter.hpp" -#include "RuntimeEnvironment.hpp" #include "MulticastGroup.hpp" #include "NonCopyable.hpp" #include "MAC.hpp" +#include "Dictionary.hpp" +#include "Identity.hpp" +#include "InetAddress.hpp" namespace ZeroTier { +class RuntimeEnvironment; class NodeConfig; /** - * Local network endpoint + * A virtual LAN + * + * Networks can be open or closed. Each network has an ID whose most + * significant 40 bits are the ZeroTier address of the node that should + * be contacted for network configuration. The least significant 24 + * bits are arbitrary, allowing up to 2^24 networks per managing + * node. + * + * Open networks do not track membership. Anyone is allowed to communicate + * over them. + * + * Closed networks track membership by way of timestamped signatures. When + * the network requests its configuration, one of the fields returned is + * a signature for the identity of the peer on the network. This signature + * includes a timestamp. When a peer communicates with other peers on a + * closed network, it periodically (and pre-emptively) propagates this + * signature to the peers with which it is communicating. Peers reject + * packets with an error if no recent signature is on file. */ class Network : NonCopyable { friend class SharedPtr<Network>; friend class NodeConfig; +public: + /** + * A certificate of network membership + */ + class Certificate : private Dictionary + { + public: + Certificate() + { + } + + Certificate(const char *s) : + Dictionary(s) + { + } + + Certificate(const std::string &s) : + Dictionary(s) + { + } + + /** + * @return Read-only underlying dictionary + */ + inline const Dictionary &dictionary() const { return *this; } + + inline void setNetworkId(uint64_t id) + { + char buf[32]; + sprintf(buf,"%.16llx",(unsigned long long)id); + (*this)["nwid"] = buf; + } + + inline uint64_t networkId() const + throw(std::invalid_argument) + { + return strtoull(get("nwid").c_str(),(char **)0,16); + } + + inline void setPeerAddress(Address &a) + { + (*this)["peer"] = a.toString(); + } + + inline Address peerAddress() const + throw(std::invalid_argument) + { + return Address(get("peer")); + } + + /** + * Set the timestamp and timestamp max-delta + * + * @param ts Timestamp in ms since epoch + * @param maxDelta Maximum difference between two peers on the same network + */ + inline void setTimestamp(uint64_t ts,uint64_t maxDelta) + { + char foo[32]; + sprintf(foo,"%llu",(unsigned long long)ts); + (*this)["ts"] = foo; + sprintf(foo,"%llu",(unsigned long long)maxDelta); + (*this)["~ts"] = foo; + } + + /** + * Sign this certificate + * + * @param with Signing identity -- the identity of this network's controller + * @return Signature or empty string on failure + */ + inline std::string sign(const Identity &with) const + { + unsigned char dig[32]; + _shaForSignature(dig); + return with.sign(dig); + } + + /** + * Verify this certificate's signature + * + * @param with Signing identity -- the identity of this network's controller + * @param sig Signature + * @param siglen Length of signature in bytes + */ + inline bool verify(const Identity &with,const void *sig,unsigned int siglen) const + { + unsigned char dig[32]; + _shaForSignature(dig); + return with.verifySignature(dig,sig,siglen); + } + + /** + * Check if another peer is indeed a current member of this network + * + * Fields with companion ~fields are compared with the defined maximum + * delta in this certificate. Fields without ~fields are compared for + * equality. + * + * This does not verify the certificate's signature! + * + * @param mc Peer membership certificate + * @return True if mc's membership in this network is current + */ + bool qualifyMembership(const Certificate &mc) const; + + private: + void _shaForSignature(unsigned char *dig) const; + }; + + /** + * A network configuration for a given node + */ + class Config : private Dictionary + { + public: + Config() + { + } + + Config(const char *s) : + Dictionary(s) + { + } + + Config(const std::string &s) : + Dictionary(s) + { + } + + inline void setNetworkId(uint64_t id) + { + char buf[32]; + sprintf(buf,"%.16llx",(unsigned long long)id); + (*this)["nwid"] = buf; + } + + inline uint64_t networkId() const + throw(std::invalid_argument) + { + return strtoull(get("nwid").c_str(),(char **)0,16); + } + + inline void setPeerAddress(Address &a) + { + (*this)["peer"] = a.toString(); + } + + inline Address peerAddress() const + throw(std::invalid_argument) + { + return Address(get("peer")); + } + + /** + * @return Certificate of membership for this network, or empty cert if none + */ + inline Certificate certificateOfMembership() const + { + return Certificate(get("com","")); + } + + /** + * @return True if this is an open non-access-controlled network + */ + inline bool isOpen() const + { + return (get("isOpen") == "1"); + } + + /** + * @return All static addresses / netmasks, IPv4 or IPv6 + */ + inline std::set<InetAddress> staticAddresses() const + { + std::set<InetAddress> sa; + std::vector<std::string> ips(Utils::split(get("ipv4Static","").c_str(),",","","")); + for(std::vector<std::string>::const_iterator i(ips.begin());i!=ips.end();++i) + sa.insert(InetAddress(*i)); + ips = Utils::split(get("ipv6Static","").c_str(),",","",""); + for(std::vector<std::string>::const_iterator i(ips.begin());i!=ips.end();++i) + sa.insert(InetAddress(*i)); + return sa; + } + + /** + * Set static IPv4 and IPv6 addresses + * + * This sets the ipv4Static and ipv6Static fields to comma-delimited + * lists of assignments. The port field in InetAddress must be the + * number of bits in the netmask. + * + * @param begin Start of container or array of addresses (InetAddress) + * @param end End of container or array of addresses (InetAddress) + * @tparam I Type of container or array + */ + template<typename I> + inline void setStaticInetAddresses(const I &begin,const I &end) + { + std::string v4; + std::string v6; + for(I i(begin);i!=end;++i) { + if (i->isV4()) { + if (v4.length()) + v4.push_back(','); + v4.append(i->toString()); + } else if (i->isV6()) { + if (v6.length()) + v6.push_back(','); + v6.append(i->toString()); + } + } + if (v4.length()) + (*this)["ipv4Static"] = v4; + else erase("ipv4Static"); + if (v6.length()) + (*this)["ipv6Static"] = v6; + else erase("ipv6Static"); + } + }; + private: + // Only NodeConfig can create, only SharedPtr can delete Network(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error); @@ -74,83 +318,101 @@ public: inline EthernetTap &tap() throw() { return _tap; } /** - * Get this network's members - * - * If this is an open network, membership isn't relevant and this doesn't - * mean much. If it's a closed network, frames will only be exchanged to/from - * members. - * - * @return Members of this network + * @return Address of network's controlling node */ - inline std::set<Address> members() const - { - Mutex::Lock _l(_lock); - return _members; - } + inline Address controller() throw() { return Address(_id >> 24); } /** - * @param addr Address to check - * @return True if address is a member + * @return True if network is open (no membership required) */ - inline bool isMember(const Address &addr) const + inline bool isOpen() const throw() { - Mutex::Lock _l(_lock); - return (_members.count(addr) > 0); + try { + Mutex::Lock _l(_lock); + return _configuration.isOpen(); + } catch ( ... ) { + return false; + } } /** - * Shortcut to check open() and then isMember() - * - * @param addr Address to check - * @return True if network is open or if address is a member + * Update multicast groups for this network's tap + * + * @return True if internal multicast group set has changed */ - inline bool isAllowed(const Address &addr) const - throw() + inline bool updateMulticastGroups() { Mutex::Lock _l(_lock); - return ((_open)||(_members.count(addr) > 0)); + return _tap.updateMulticastGroups(_multicastGroups); } /** - * @return True if network is open (no membership required) + * @return Latest set of multicast groups for this network's tap */ - inline bool open() const - throw() + inline std::set<MulticastGroup> multicastGroups() const { Mutex::Lock _l(_lock); - return _open; + return _multicastGroups; } /** - * Update internal multicast group set and return true if changed + * Set or update this network's configuration * - * @return True if internal multicast group set has changed + * This is called by PacketDecoder when an update comes over the wire, or + * internally when an old config is reloaded from disk. + * + * @param conf Configuration in key/value dictionary form */ - inline bool updateMulticastGroups() + void setConfiguration(const Config &conf); + + /** + * Causes this network to request an updated configuration from its master node now + */ + void requestConfiguration(); + + /** + * Add or update a peer's membership certificate + * + * The certificate must already have been validated via signature checking. + * + * @param peer Peer that owns certificate + * @param cert Certificate itself + */ + inline void addMembershipCertificate(const Address &peer,const Certificate &cert) { Mutex::Lock _l(_lock); - return _tap.updateMulticastGroups(_multicastGroups); + _membershipCertificates[peer] = cert; } + bool isAllowed(const Address &peer) const; + /** - * @return Latest set of multicast groups + * Perform periodic database cleaning such as removing expired membership certificates */ - inline std::set<MulticastGroup> multicastGroups() const + void clean(); + + /** + * @return Time of last updated configuration or 0 if none + */ + inline uint64_t lastConfigUpdate() const + throw() { - Mutex::Lock _l(_lock); - return _multicastGroups; + return _lastConfigUpdate; } private: static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data); const RuntimeEnvironment *_r; - uint64_t _id; + EthernetTap _tap; - std::set<Address> _members; std::set<MulticastGroup> _multicastGroups; - bool _open; + std::map<Address,Certificate> _membershipCertificates; + Config _configuration; + Certificate _myCertificate; + uint64_t _lastConfigUpdate; + uint64_t _id; Mutex _lock; AtomicCounter __refCount; |