/* * ZeroTier One - Global Peer to Peer Ethernet * Copyright (C) 2012-2013 ZeroTier Networks LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * -- * * ZeroTier may be used and distributed under the terms of the GPLv3, which * are available at: http://www.gnu.org/licenses/gpl-3.0.html * * If you would like to embed ZeroTier into a commercial application or * redistribute it in a modified binary form, please contact ZeroTier Networks * LLC. Start here: http://www.zerotier.com/ */ #ifndef _ZT_NETWORK_HPP #define _ZT_NETWORK_HPP #include #include #include #include #include #include #include #include "Constants.hpp" #include "Utils.hpp" #include "EthernetTap.hpp" #include "Address.hpp" #include "Mutex.hpp" #include "SharedPtr.hpp" #include "AtomicCounter.hpp" #include "MulticastGroup.hpp" #include "NonCopyable.hpp" #include "MAC.hpp" #include "Dictionary.hpp" #include "Identity.hpp" #include "InetAddress.hpp" #include "BandwidthAccount.hpp" #include "C25519.hpp" namespace ZeroTier { class RuntimeEnvironment; class NodeConfig; /** * 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; friend class NodeConfig; public: /** * Certificate of network membership * * The COM contains a sorted set of three-element tuples called qualifiers. * These contain an id, a value, and a maximum delta. * * The ID is arbitrary and should be assigned using a scheme that makes * every ID globally unique. ID 0 is reserved for the always-present * validity timestamp and range, and ID 1 is reserved for the always-present * network ID. IDs less than 65536 are reserved for future global * assignment. * * The value's meaning is ID-specific and isn't important here. What's * important is the value and the third member of the tuple: the maximum * delta. The maximum delta is the maximum difference permitted between * values for a given ID between certificates for the two certificates to * themselves agree. * * Network membership is checked by checking whether a peer's certificate * agrees with your own. The timestamp provides the fundamental criterion-- * each member of a private network must constantly obtain new certificates * often enough to stay within the max delta for this qualifier. But other * criteria could be added in the future for very special behaviors, things * like latitude and longitude for instance. */ class CertificateOfMembership { public: /** * Certificate type codes, used in serialization * * Only one so far, and only one hopefully there shall be for quite some * time. */ enum Type { COM_UINT64_ED25519 = 1 // tuples of unsigned 64's signed with Ed25519 }; /** * Reserved COM IDs * * IDs below 65536 should be considered reserved for future global * assignment here. */ enum ReservedIds { COM_RESERVED_ID_TIMESTAMP = 0, // timestamp, max delta defines cert life COM_RESERVED_ID_NETWORK_ID = 1 // network ID, max delta always 0 }; CertificateOfMembership() {} CertificateOfMembership(const char *s) { fromString(s); } CertificateOfMembership(const std::string &s) { fromString(s.c_str()); } /** * Add or update a qualifier in this certificate * * Any signature is invalidated and signedBy is set to null. * * @param id Qualifier ID * @param value Qualifier value * @param maxDelta Qualifier maximum allowed difference (absolute value of difference) */ void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta); /** * @return String-serialized representation of this certificate */ std::string toString() const; /** * Set this certificate equal to the hex-serialized string * * Invalid strings will result in invalid or undefined certificate * contents. These will subsequently fail validation and comparison. * * @param s String to deserialize */ void fromString(const char *s); inline void fromString(const std::string &s) { fromString(s.c_str()); } /** * Compare two certificates for parameter agreement * * This compares this certificate with the other and returns true if all * paramters in this cert are present in the other and if they agree to * within this cert's max delta value for each given parameter. * * Tuples present in other but not in this cert are ignored, but any * tuples present in this cert but not in other result in 'false'. * * @param other Cert to compare with * @return True if certs agree and 'other' may be communicated with */ bool agreesWith(const CertificateOfMembership &other) const throw(); /** * Sign this certificate * * @param with Identity to sign with, must include private key * @return True if signature was successful */ bool sign(const Identity &with); /** * Verify certificate against an identity * * @param id Identity to verify against * @return True if certificate is signed by this identity and verification was successful */ bool verify(const Identity &id) const; /** * @return True if signed */ inline bool isSigned() const throw() { return (_signedBy); } /** * @return Address that signed this certificate or null address if none */ inline const Address &signedBy() const throw() { return _signedBy; } private: struct _Qualifier { _Qualifier() throw() {} _Qualifier(uint64_t i,uint64_t v,uint64_t m) throw() : id(i), value(v), maxDelta(m) {} uint64_t id; uint64_t value; uint64_t maxDelta; inline bool operator==(const _Qualifier &q) const throw() { return (id == q.id); } // for unique inline bool operator<(const _Qualifier &q) const throw() { return (id < q.id); } // for sort }; std::vector<_Qualifier> _qualifiers; // sorted by id and unique Address _signedBy; C25519::Signature _signature; }; /** * Preload and rates of accrual for multicast group bandwidth limits * * Key is multicast group in lower case hex format: MAC (without :s) / * ADI (hex). Value is preload, maximum balance, and rate of accrual in * hex. */ class MulticastRates : private Dictionary { public: /** * Preload and accrual parameter tuple */ struct Rate { Rate() {} Rate(uint32_t pl,uint32_t maxb,uint32_t acc) { preload = pl; maxBalance = maxb; accrual = acc; } uint32_t preload; uint32_t maxBalance; uint32_t accrual; }; MulticastRates() {} MulticastRates(const char *s) : Dictionary(s) {} MulticastRates(const std::string &s) : Dictionary(s) {} inline std::string toString() const { return Dictionary::toString(); } /** * A very minimal default rate, fast enough for ARP */ static const Rate GLOBAL_DEFAULT_RATE; /** * @return Default rate, or GLOBAL_DEFAULT_RATE if not specified */ inline Rate defaultRate() const { Rate r; const_iterator dfl(find("*")); if (dfl == end()) return GLOBAL_DEFAULT_RATE; return _toRate(dfl->second); } /** * Get the rate for a given multicast group * * @param mg Multicast group * @return Rate or default() rate if not specified */ inline Rate get(const MulticastGroup &mg) const { const_iterator r(find(mg.toString())); if (r == end()) return defaultRate(); return _toRate(r->second); } private: static inline Rate _toRate(const std::string &s) { char tmp[16384]; Utils::scopy(tmp,sizeof(tmp),s.c_str()); Rate r(0,0,0); char *saveptr = (char *)0; unsigned int fn = 0; for(char *f=Utils::stok(tmp,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) { switch(fn++) { case 0: r.preload = (uint32_t)Utils::hexStrToULong(f); break; case 1: r.maxBalance = (uint32_t)Utils::hexStrToULong(f); break; case 2: r.accrual = (uint32_t)Utils::hexStrToULong(f); break; } } return r; } }; /** * A network configuration for a given node * * Configuration fields: * * nwid= (required) * name=short name * desc=long(er) description * com=Serialized certificate of membership * mr=MulticastRates (serialized dictionary) * md=multicast propagation depth * mpb=multicast propagation prefix bits (2^mpb packets are sent by origin) * o=open network? (1 or 0, default false if missing) * et=ethertype whitelist (comma-delimited list of ethertypes in decimal) * v4s=IPv4 static assignments / netmasks (comma-delimited) * v6s=IPv6 static assignments / netmasks (comma-delimited) * * Notes: * * If zero appears in the 'et' list, the sense is inverted. It becomes an * ethertype blacklist instead of a whitelist and anything not blacklisted * is permitted. */ class Config : private Dictionary { public: Config() {} Config(const char *s) : Dictionary(s) {} Config(const std::string &s) : Dictionary(s) {} inline std::string toString() const { return Dictionary::toString(); } /** * @return True if configuration is valid and contains required fields */ inline operator bool() const throw() { return (find("nwid") != end()); } /** * @return Network ID * @throws std::invalid_argument Network ID field missing */ inline uint64_t networkId() const throw(std::invalid_argument) { return Utils::hexStrToU64(get("nwid").c_str()); } /** * Get this network's short name, or its ID in hex if unspecified * * @return Short name of this network (e.g. "earth") */ inline std::string name() const { const_iterator n(find("name")); if (n == end()) return get("nwid"); return n->second; } /** * @return Long description of network or empty string if not present */ inline std::string desc() const { return get("desc",std::string()); } /** * @return Certificate of membership for this network, or empty cert if none */ inline CertificateOfMembership certificateOfMembership() const { const_iterator cm(find("com")); if (cm == end()) return CertificateOfMembership(); else return CertificateOfMembership(cm->second); } /** * @return Multicast rates for this network */ inline MulticastRates multicastRates() const { const_iterator mr(find("mr")); if (mr == end()) return MulticastRates(); else return MulticastRates(mr->second); } /** * @return Number of bits in propagation prefix for this network */ inline unsigned int multicastPrefixBits() const { const_iterator mpb(find("mpb")); if (mpb == end()) return ZT_DEFAULT_MULTICAST_PREFIX_BITS; unsigned int tmp = Utils::hexStrToUInt(mpb->second.c_str()); if (tmp) return tmp; else return ZT_DEFAULT_MULTICAST_PREFIX_BITS; } /** * @return Maximum multicast propagation depth for this network */ inline unsigned int multicastDepth() const { const_iterator md(find("md")); if (md == end()) return ZT_DEFAULT_MULTICAST_DEPTH; unsigned int tmp = Utils::hexStrToUInt(md->second.c_str()); if (tmp) return tmp; else return ZT_DEFAULT_MULTICAST_DEPTH; } /** * @return True if this is an open non-access-controlled network */ inline bool isOpen() const { const_iterator o(find("o")); if (o == end()) return false; else if (!o->second.length()) return false; else return (o->second[0] == '1'); } /** * @return Network ethertype whitelist */ inline std::set etherTypes() const { char tmp[16384]; char *saveptr = (char *)0; std::set et; if (!Utils::scopy(tmp,sizeof(tmp),get("et","").c_str())) return et; // sanity check, packet can't really be that big for(char *f=Utils::stok(tmp,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) { unsigned int t = Utils::hexStrToUInt(f); if (t) et.insert(t); } return et; } /** * @return All static addresses / netmasks, IPv4 or IPv6 */ inline std::set staticAddresses() const { std::set sa; std::vector ips(Utils::split(get("v4s","").c_str(),",","","")); for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) sa.insert(InetAddress(*i)); ips = Utils::split(get("v6s","").c_str(),",","",""); for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) sa.insert(InetAddress(*i)); return sa; } }; /** * Status for networks */ enum Status { NETWORK_WAITING_FOR_FIRST_AUTOCONF, NETWORK_OK, NETWORK_ACCESS_DENIED }; /** * @param s Status * @return String description */ static const char *statusString(const Status s) throw(); private: // Only NodeConfig can create, only SharedPtr can delete // Actual construction happens in newInstance() Network() throw() : _tap((EthernetTap *)0) { } ~Network(); /** * Create a new Network instance and restore any saved state * * If there is no saved state, a dummy .conf is created on disk to remember * this network across restarts. * * @param renv Runtime environment * @param id Network ID * @return Reference counted pointer to new network * @throws std::runtime_error Unable to create tap device or other fatal error */ static SharedPtr newInstance(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error); /** * Causes all persistent disk presence to be erased on delete */ inline void destroyOnDelete() throw() { _destroyOnDelete = true; } public: /** * @return Network ID */ inline uint64_t id() const throw() { return _id; } /** * @return Ethernet tap */ inline EthernetTap &tap() throw() { return *_tap; } /** * @return Address of network's controlling node */ inline Address controller() throw() { return Address(_id >> 24); } /** * @return Network ID in hexadecimal form */ inline std::string idString() { char buf[64]; Utils::snprintf(buf,sizeof(buf),"%.16llx",(unsigned long long)_id); return std::string(buf); } /** * @return True if network is open (no membership required) */ inline bool isOpen() const throw() { Mutex::Lock _l(_lock); return _isOpen; } /** * Update multicast groups for this network's tap * * @return True if internal multicast group set has changed */ inline bool updateMulticastGroups() { Mutex::Lock _l(_lock); return _tap->updateMulticastGroups(_multicastGroups); } /** * @return Latest set of multicast groups for this network's tap */ inline std::set multicastGroups() const { Mutex::Lock _l(_lock); return _multicastGroups; } /** * Set or update this network's configuration * * 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 */ 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 */ void addMembershipCertificate(const Address &peer,const CertificateOfMembership &cert); /** * @param peer Peer address to check * @return True if peer is allowed to communicate on this network */ bool isAllowed(const Address &peer) const; /** * Perform cleanup and possibly save state */ void clean(); /** * @return Time of last updated configuration or 0 if none */ inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; } /** * @return Status of this network */ Status status() const; /** * Determine whether frames of a given ethernet type are allowed on this network * * @param etherType Ethernet frame type * @return True if network permits this type */ inline bool permitsEtherType(unsigned int etherType) const throw() { if (!etherType) return false; else if (etherType > 65535) return false; else if ((_etWhitelist[0] & 1)) // if type 0 is in the whitelist, sense is inverted from whitelist to blacklist return ((_etWhitelist[etherType / 8] & (unsigned char)(1 << (etherType & 7))) == 0); else return ((_etWhitelist[etherType / 8] & (unsigned char)(1 << (etherType & 7))) != 0); } /** * Update multicast balance for an address and multicast group, return whether packet is allowed * * @param a Address that wants to send/relay packet * @param mg Multicast group * @param bytes Size of packet * @return True if packet is within budget */ inline bool updateAndCheckMulticastBalance(const Address &a,const MulticastGroup &mg,unsigned int bytes) { Mutex::Lock _l(_lock); std::pair k(a,mg); std::map< std::pair,BandwidthAccount >::iterator bal(_multicastRateAccounts.find(k)); if (bal == _multicastRateAccounts.end()) { MulticastRates::Rate r(_mcRates.get(mg)); bal = _multicastRateAccounts.insert(std::pair< std::pair,BandwidthAccount >(k,BandwidthAccount(r.preload,r.maxBalance,r.accrual))).first; } return bal->second.deduct(bytes); //bool tmp = bal->second.deduct(bytes); //printf("%s: BAL: %u\n",mg.toString().c_str(),(unsigned int)bal->second.balance()); //return tmp; } /** * @return True if this network allows bridging */ inline bool permitsBridging() const throw() { return false; // TODO: bridging not implemented yet } /** * @return Bits in multicast restriciton prefix */ inline unsigned int multicastPrefixBits() const throw() { return _multicastPrefixBits; } /** * @return Max depth (TTL) for a multicast frame */ inline unsigned int multicastDepth() const throw() { return _multicastDepth; } private: static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data); void _restoreState(); const RuntimeEnvironment *_r; // Multicast bandwidth accounting for peers on this network std::map< std::pair,BandwidthAccount > _multicastRateAccounts; // Tap and tap multicast memberships for this node on this network EthernetTap *_tap; std::set _multicastGroups; // Membership certificates supplied by other peers on this network std::map _membershipCertificates; // Configuration from network master node Config _configuration; CertificateOfMembership _myCertificate; // memoized from _configuration MulticastRates _mcRates; // memoized from _configuration std::set _staticAddresses; // memoized from _configuration bool _isOpen; // memoized from _configuration unsigned int _multicastPrefixBits; // memoized from _configuration unsigned int _multicastDepth; // memoized from _configuration // Ethertype whitelist bit field, set from config, for really fast lookup unsigned char _etWhitelist[65536 / 8]; uint64_t _id; volatile uint64_t _lastConfigUpdate; volatile bool _destroyOnDelete; volatile bool _ready; Mutex _lock; AtomicCounter __refCount; }; } // naemspace ZeroTier #endif