summaryrefslogtreecommitdiff
path: root/node/Network.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/Network.hpp')
-rw-r--r--node/Network.hpp348
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;