mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 12:33:44 +02:00
Network membership certificate work in progress... does not build yet.
This commit is contained in:
parent
e7b515c86c
commit
a53cfc9096
12 changed files with 470 additions and 77 deletions
|
@ -20,7 +20,7 @@ CXXFLAGS=$(CFLAGS) -fno-rtti
|
||||||
# separate binaries for the RedHat and Debian universes to distribute via
|
# separate binaries for the RedHat and Debian universes to distribute via
|
||||||
# auto-update. This way we get one Linux binary for all systems of a given
|
# auto-update. This way we get one Linux binary for all systems of a given
|
||||||
# architecture.
|
# architecture.
|
||||||
LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -ldl
|
LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -lm
|
||||||
|
|
||||||
include objects.mk
|
include objects.mk
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ STRIP=strip
|
||||||
#STRIP=echo
|
#STRIP=echo
|
||||||
|
|
||||||
CXXFLAGS=$(CFLAGS) -fno-rtti
|
CXXFLAGS=$(CFLAGS) -fno-rtti
|
||||||
LIBS=-lcrypto -ldl
|
LIBS=-lcrypto -lm
|
||||||
|
|
||||||
include objects.mk
|
include objects.mk
|
||||||
|
|
||||||
|
|
|
@ -64,13 +64,28 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
Address(const char *s)
|
||||||
* @param bits Raw address -- 5 bytes, big-endian byte order
|
|
||||||
*/
|
|
||||||
Address(const void *bits)
|
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
setTo(bits);
|
unsigned char foo[ZT_ADDRESS_LENGTH];
|
||||||
|
setTo(foo,Utils::unhex(s,foo,ZT_ADDRESS_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
Address(const std::string &s)
|
||||||
|
throw()
|
||||||
|
{
|
||||||
|
unsigned char foo[ZT_ADDRESS_LENGTH];
|
||||||
|
setTo(foo,Utils::unhex(s.c_str(),foo,ZT_ADDRESS_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bits Raw address -- 5 bytes, big-endian byte order
|
||||||
|
* @param len Length of array
|
||||||
|
*/
|
||||||
|
Address(const void *bits,unsigned int len)
|
||||||
|
throw()
|
||||||
|
{
|
||||||
|
setTo(bits,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Address &operator=(const Address &a)
|
inline Address &operator=(const Address &a)
|
||||||
|
@ -89,10 +104,15 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bits Raw address -- 5 bytes, big-endian byte order
|
* @param bits Raw address -- 5 bytes, big-endian byte order
|
||||||
|
* @param len Length of array
|
||||||
*/
|
*/
|
||||||
inline void setTo(const void *bits)
|
inline void setTo(const void *bits,unsigned int len)
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
|
if (len < ZT_ADDRESS_LENGTH) {
|
||||||
|
_a = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const unsigned char *b = (const unsigned char *)bits;
|
const unsigned char *b = (const unsigned char *)bits;
|
||||||
uint64_t a = ((uint64_t)*b++) << 32;
|
uint64_t a = ((uint64_t)*b++) << 32;
|
||||||
a |= ((uint64_t)*b++) << 24;
|
a |= ((uint64_t)*b++) << 24;
|
||||||
|
@ -104,10 +124,13 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bits Buffer to hold 5-byte address in big-endian byte order
|
* @param bits Buffer to hold 5-byte address in big-endian byte order
|
||||||
|
* @param len Length of array
|
||||||
*/
|
*/
|
||||||
inline void copyTo(void *bits) const
|
inline void copyTo(void *bits,unsigned int len) const
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
|
if (len < ZT_ADDRESS_LENGTH)
|
||||||
|
return;
|
||||||
unsigned char *b = (unsigned char *)bits;
|
unsigned char *b = (unsigned char *)bits;
|
||||||
*(b++) = (unsigned char)((_a >> 32) & 0xff);
|
*(b++) = (unsigned char)((_a >> 32) & 0xff);
|
||||||
*(b++) = (unsigned char)((_a >> 24) & 0xff);
|
*(b++) = (unsigned char)((_a >> 24) & 0xff);
|
||||||
|
@ -164,7 +187,8 @@ public:
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
MAC m;
|
MAC m;
|
||||||
copyTo(m.data);
|
m.data[0] = ZT_MAC_FIRST_OCTET;
|
||||||
|
copyTo(m.data + 1,ZT_ADDRESS_LENGTH);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
@ -38,11 +39,76 @@ namespace ZeroTier {
|
||||||
* Simple key/value dictionary with string serialization
|
* Simple key/value dictionary with string serialization
|
||||||
*
|
*
|
||||||
* The serialization format is a flat key=value with backslash escape.
|
* The serialization format is a flat key=value with backslash escape.
|
||||||
* It does not support comments or other syntactic complexities.
|
* It does not support comments or other syntactic complexities. It is
|
||||||
|
* human-readable if the keys and values in the dictionary are also
|
||||||
|
* human-readable. Otherwise it might contain unprintable characters.
|
||||||
*/
|
*/
|
||||||
class Dictionary : public std::map<std::string,std::string>
|
class Dictionary : public std::map<std::string,std::string>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Dictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s String-serialized dictionary
|
||||||
|
*/
|
||||||
|
Dictionary(const char *s)
|
||||||
|
{
|
||||||
|
fromString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s String-serialized dictionary
|
||||||
|
*/
|
||||||
|
Dictionary(const std::string &s)
|
||||||
|
{
|
||||||
|
fromString(s.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a key, throwing an exception if it is not present
|
||||||
|
*
|
||||||
|
* @param key Key to look up
|
||||||
|
* @return Reference to value
|
||||||
|
* @throws std::invalid_argument Key not found
|
||||||
|
*/
|
||||||
|
inline const std::string &get(const std::string &key) const
|
||||||
|
throw(std::invalid_argument)
|
||||||
|
{
|
||||||
|
const_iterator e(find(key));
|
||||||
|
if (e == end())
|
||||||
|
throw std::invalid_argument(std::string("missing required field: ")+key);
|
||||||
|
return e->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a key, returning a default if not present
|
||||||
|
*
|
||||||
|
* @param key Key to look up
|
||||||
|
* @param dfl Default if not present
|
||||||
|
* @return Value or default
|
||||||
|
*/
|
||||||
|
inline const std::string &get(const std::string &key,const std::string &dfl) const
|
||||||
|
{
|
||||||
|
const_iterator e(find(key));
|
||||||
|
if (e == end())
|
||||||
|
return dfl;
|
||||||
|
return e->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to check
|
||||||
|
* @return True if dictionary contains key
|
||||||
|
*/
|
||||||
|
inline bool contains(const std::string &key) const
|
||||||
|
{
|
||||||
|
return (find(key) != end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return String-serialized dictionary
|
||||||
|
*/
|
||||||
inline std::string toString() const
|
inline std::string toString() const
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
|
@ -57,6 +123,11 @@ public:
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear and initialize from a string
|
||||||
|
*
|
||||||
|
* @param s String-serialized dictionary
|
||||||
|
*/
|
||||||
inline void fromString(const char *s)
|
inline void fromString(const char *s)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
|
@ -58,7 +58,7 @@ void Identity::generate()
|
||||||
// invalid. Of course, deep verification of address/key relationship is
|
// invalid. Of course, deep verification of address/key relationship is
|
||||||
// required to cover the more elaborate address claim jump attempt case.
|
// required to cover the more elaborate address claim jump attempt case.
|
||||||
unsigned char atmp[ZT_ADDRESS_LENGTH];
|
unsigned char atmp[ZT_ADDRESS_LENGTH];
|
||||||
_address.copyTo(atmp);
|
_address.copyTo(atmp,ZT_ADDRESS_LENGTH);
|
||||||
SHA256_CTX sha;
|
SHA256_CTX sha;
|
||||||
unsigned char dig[32];
|
unsigned char dig[32];
|
||||||
unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
|
unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
|
||||||
|
@ -76,7 +76,7 @@ void Identity::generate()
|
||||||
bool Identity::locallyValidate(bool doAddressDerivationCheck) const
|
bool Identity::locallyValidate(bool doAddressDerivationCheck) const
|
||||||
{
|
{
|
||||||
unsigned char atmp[ZT_ADDRESS_LENGTH];
|
unsigned char atmp[ZT_ADDRESS_LENGTH];
|
||||||
_address.copyTo(atmp);
|
_address.copyTo(atmp,ZT_ADDRESS_LENGTH);
|
||||||
SHA256_CTX sha;
|
SHA256_CTX sha;
|
||||||
unsigned char dig[32];
|
unsigned char dig[32];
|
||||||
unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
|
unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
|
||||||
|
@ -222,7 +222,7 @@ Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
|
||||||
|
|
||||||
delete [] ram;
|
delete [] ram;
|
||||||
|
|
||||||
return Address(dig); // first 5 bytes of dig[]
|
return Address(dig,ZT_ADDRESS_LENGTH); // first 5 bytes of dig[]
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Identity::encrypt(const Identity &to,const void *data,unsigned int len) const
|
std::string Identity::encrypt(const Identity &to,const void *data,unsigned int len) const
|
||||||
|
|
|
@ -25,19 +25,90 @@
|
||||||
* LLC. Start here: http://www.zerotier.com/
|
* LLC. Start here: http://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
#include "NodeConfig.hpp"
|
||||||
#include "Network.hpp"
|
#include "Network.hpp"
|
||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
void Network::Certificate::sign(const Identity &with)
|
||||||
|
{
|
||||||
|
unsigned char dig[32];
|
||||||
|
SHA256_CTX sha;
|
||||||
|
SHA256_Init(&sha);
|
||||||
|
unsigned char zero = 0;
|
||||||
|
for(const_iterator i(begin());i!=end();++i) {
|
||||||
|
if (i->first != "sig") {
|
||||||
|
SHA256_Update(&sha,&zero,1);
|
||||||
|
SHA256_Update(&sha,(const unsigned char *)i->first.data(),i->first.length());
|
||||||
|
SHA256_Update(&sha,&zero,1);
|
||||||
|
SHA256_Update(&sha,(const unsigned char *)i->second.data(),i->second.length());
|
||||||
|
SHA256_Update(&sha,&zero,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SHA256_Final(dig,&sha);
|
||||||
|
(*this)["sig"] = with.sign(dig);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string _DELTA_PREFIX("~");
|
||||||
|
bool Network::Certificate::qualifyMembership(const Network::Certificate &mc) const
|
||||||
|
{
|
||||||
|
// Note: optimization probably needed here, probably via some kind of
|
||||||
|
// memoization / dynamic programming.
|
||||||
|
|
||||||
|
for(const_iterator myField(begin());myField!=end();++myField) {
|
||||||
|
if (!((myField->first.length() > 1)&&(myField->first[0] == '~'))) { // ~fields are max delta range specs
|
||||||
|
// If they lack the same field, comparison fails.
|
||||||
|
const_iterator theirField(mc.find(myField->first));
|
||||||
|
if (theirField == mc.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const_iterator deltaField(find(_DELTA_PREFIX + myField->first));
|
||||||
|
if (deltaField == end()) {
|
||||||
|
// If there is no delta, compare for equality (e.g. node, nwid)
|
||||||
|
if (myField->second != theirField->second)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Otherwise compare range with max delta. Presence of a dot in delta
|
||||||
|
// indicates a floating point comparison. Otherwise an integer
|
||||||
|
// comparison occurs.
|
||||||
|
if (deltaField->second.find('.') != std::string::npos) {
|
||||||
|
double my = strtod(myField->second.c_str(),(char **)0);
|
||||||
|
double their = strtod(theirField->second.c_str(),(char **)0);
|
||||||
|
double delta = strtod(deltaField->second.c_str(),(char **)0);
|
||||||
|
if (fabs(my - their) > delta)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
int64_t my = strtoll(myField->second.c_str(),(char **)0,10);
|
||||||
|
int64_t their = strtoll(theirField->second.c_str(),(char **)0,10);
|
||||||
|
int64_t delta = strtoll(deltaField->second.c_str(),(char **)0,10);
|
||||||
|
if (my > their) {
|
||||||
|
if ((my - their) > delta)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if ((their - my) > delta)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Network::Network(const RuntimeEnvironment *renv,uint64_t id)
|
Network::Network(const RuntimeEnvironment *renv,uint64_t id)
|
||||||
throw(std::runtime_error) :
|
throw(std::runtime_error) :
|
||||||
_r(renv),
|
_r(renv),
|
||||||
_id(id),
|
|
||||||
_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
|
_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
|
||||||
_members(),
|
_id(id),
|
||||||
_open(false),
|
_isOpen(false)
|
||||||
_lock()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +116,14 @@ Network::~Network()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Network::setConfiguration(const Network::Config &conf)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Network::requestConfiguration()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
|
void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
|
||||||
{
|
{
|
||||||
const RuntimeEnvironment *_r = ((Network *)arg)->_r;
|
const RuntimeEnvironment *_r = ((Network *)arg)->_r;
|
||||||
|
|
262
node/Network.hpp
262
node/Network.hpp
|
@ -30,28 +30,37 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "Constants.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
#include "EthernetTap.hpp"
|
#include "EthernetTap.hpp"
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "InetAddress.hpp"
|
|
||||||
#include "Constants.hpp"
|
|
||||||
#include "SharedPtr.hpp"
|
#include "SharedPtr.hpp"
|
||||||
#include "AtomicCounter.hpp"
|
#include "AtomicCounter.hpp"
|
||||||
#include "RuntimeEnvironment.hpp"
|
|
||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
#include "NonCopyable.hpp"
|
#include "NonCopyable.hpp"
|
||||||
#include "MAC.hpp"
|
#include "MAC.hpp"
|
||||||
|
#include "Dictionary.hpp"
|
||||||
|
#include "Identity.hpp"
|
||||||
|
#include "InetAddress.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
class RuntimeEnvironment;
|
||||||
class NodeConfig;
|
class NodeConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A virtual LAN
|
* A virtual LAN
|
||||||
*
|
*
|
||||||
* Networks can be open or closed.
|
* 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
|
* Open networks do not track membership. Anyone is allowed to communicate
|
||||||
* over them.
|
* over them.
|
||||||
|
@ -69,7 +78,183 @@ class Network : NonCopyable
|
||||||
friend class SharedPtr<Network>;
|
friend class SharedPtr<Network>;
|
||||||
friend class NodeConfig;
|
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,"%llu",id);
|
||||||
|
(*this)["nwid"] = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t networkId() const
|
||||||
|
throw(std::invalid_argument)
|
||||||
|
{
|
||||||
|
return strtoull(get("nwid").c_str(),(char **)0,10);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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",ts);
|
||||||
|
(*this)["ts"] = foo;
|
||||||
|
sprintf(foo,"%llu",maxDelta);
|
||||||
|
(*this)["~ts"] = foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or update the sig field to contain a signature
|
||||||
|
*
|
||||||
|
* @param with Signing identity -- the identity of this network's controller
|
||||||
|
*/
|
||||||
|
void sign(const Identity &with);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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! The signature
|
||||||
|
* must be verified first.
|
||||||
|
*
|
||||||
|
* @param mc Peer membership certificate
|
||||||
|
* @return True if mc's membership in this network is current
|
||||||
|
*/
|
||||||
|
bool qualifyMembership(const Certificate &mc) 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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","0") == "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:
|
private:
|
||||||
|
// Only NodeConfig can create, only SharedPtr can delete
|
||||||
Network(const RuntimeEnvironment *renv,uint64_t id)
|
Network(const RuntimeEnvironment *renv,uint64_t id)
|
||||||
throw(std::runtime_error);
|
throw(std::runtime_error);
|
||||||
|
|
||||||
|
@ -87,56 +272,22 @@ public:
|
||||||
inline EthernetTap &tap() throw() { return _tap; }
|
inline EthernetTap &tap() throw() { return _tap; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this network's members
|
* @return Address of network's controlling node
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
inline std::set<Address> members() const
|
inline Address controller() throw() { return Address(_id >> 24); }
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _members;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param addr Address to check
|
|
||||||
* @return True if address is a member
|
|
||||||
*/
|
|
||||||
inline bool isMember(const Address &addr) const
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return (_members.count(addr) > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shortcut to check open() and then isMember()
|
|
||||||
*
|
|
||||||
* @param addr Address to check
|
|
||||||
* @return True if network is open or if address is a member
|
|
||||||
*/
|
|
||||||
inline bool isAllowed(const Address &addr) const
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return ((_open)||(_members.count(addr) > 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if network is open (no membership required)
|
* @return True if network is open (no membership required)
|
||||||
*/
|
*/
|
||||||
inline bool open() const
|
inline bool isOpen() const
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
return _open;
|
return _isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update internal multicast group set and return true if changed
|
* Update multicast groups for this network's tap
|
||||||
*
|
*
|
||||||
* @return True if internal multicast group set has changed
|
* @return True if internal multicast group set has changed
|
||||||
*/
|
*/
|
||||||
|
@ -147,7 +298,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Latest set of multicast groups
|
* @return Latest set of multicast groups for this network's tap
|
||||||
*/
|
*/
|
||||||
inline std::set<MulticastGroup> multicastGroups() const
|
inline std::set<MulticastGroup> multicastGroups() const
|
||||||
{
|
{
|
||||||
|
@ -155,15 +306,32 @@ public:
|
||||||
return _multicastGroups;
|
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();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
|
static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
|
||||||
|
|
||||||
const RuntimeEnvironment *_r;
|
const RuntimeEnvironment *_r;
|
||||||
uint64_t _id;
|
|
||||||
EthernetTap _tap;
|
EthernetTap _tap;
|
||||||
std::set<Address> _members;
|
|
||||||
std::set<MulticastGroup> _multicastGroups;
|
std::set<MulticastGroup> _multicastGroups;
|
||||||
bool _open;
|
|
||||||
|
uint64_t _id;
|
||||||
|
bool _isOpen;
|
||||||
|
|
||||||
Mutex _lock;
|
Mutex _lock;
|
||||||
|
|
||||||
AtomicCounter __refCount;
|
AtomicCounter __refCount;
|
||||||
|
|
|
@ -126,7 +126,7 @@ std::vector<std::string> NodeConfig::execute(const char *command)
|
||||||
_P("200 listnetworks %llu %s %s",
|
_P("200 listnetworks %llu %s %s",
|
||||||
nw->first,
|
nw->first,
|
||||||
nw->second->tap().deviceName().c_str(),
|
nw->second->tap().deviceName().c_str(),
|
||||||
(nw->second->open() ? "public" : "private"));
|
(nw->second->isOpen() ? "public" : "private"));
|
||||||
}
|
}
|
||||||
} else if (cmd[0] == "join") {
|
} else if (cmd[0] == "join") {
|
||||||
_P("404 join Not implemented yet.");
|
_P("404 join Not implemented yet.");
|
||||||
|
|
|
@ -43,6 +43,8 @@ const char *Packet::verbString(Verb v)
|
||||||
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
|
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
|
||||||
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
|
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
|
||||||
case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE";
|
case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE";
|
||||||
|
case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
|
||||||
|
case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
|
||||||
}
|
}
|
||||||
return "(unknown)";
|
return "(unknown)";
|
||||||
}
|
}
|
||||||
|
@ -59,7 +61,6 @@ const char *Packet::errorString(ErrorCode e)
|
||||||
case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID";
|
case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID";
|
||||||
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
|
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
|
||||||
case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE";
|
case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE";
|
||||||
case ERROR_OBJECT_EXPIRED: return "OBJECT_EXPIRED";
|
|
||||||
}
|
}
|
||||||
return "(unknown)";
|
return "(unknown)";
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,7 +287,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return Destination ZT address
|
* @return Destination ZT address
|
||||||
*/
|
*/
|
||||||
inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH)); }
|
inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if fragment is of a valid length
|
* @return True if fragment is of a valid length
|
||||||
|
@ -468,8 +468,9 @@ public:
|
||||||
/* Network permission certificate:
|
/* Network permission certificate:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[1] flags (currently unused, must be 0)>
|
* <[1] flags (currently unused, must be 0)>
|
||||||
* <[8] certificate timestamp>
|
* <[2] 16-bit length of qualifying fields>
|
||||||
* <[8] 16-bit length of signature>
|
* <[...] string-serialized dictionary of qualifying fields>
|
||||||
|
* <[2] 16-bit length of signature>
|
||||||
* <[...] ECDSA signature of my binary serialized identity and timestamp>
|
* <[...] ECDSA signature of my binary serialized identity and timestamp>
|
||||||
*
|
*
|
||||||
* This message is used to send ahead of time a certificate proving
|
* This message is used to send ahead of time a certificate proving
|
||||||
|
@ -478,7 +479,38 @@ public:
|
||||||
* OK is generated on acceptance. ERROR is returned on failure. In both
|
* OK is generated on acceptance. ERROR is returned on failure. In both
|
||||||
* cases the payload is the network ID.
|
* cases the payload is the network ID.
|
||||||
*/
|
*/
|
||||||
VERB_NETWORK_PERMISSION_CERTIFICATE = 10
|
VERB_NETWORK_PERMISSION_CERTIFICATE = 10,
|
||||||
|
|
||||||
|
/* Network configuration request:
|
||||||
|
* <[8] 64-bit network ID>
|
||||||
|
*
|
||||||
|
* This message requests network configuration from a node capable of
|
||||||
|
* providing it. Such nodes run the netconf service, which must be
|
||||||
|
* installed into the ZeroTier home directory.
|
||||||
|
*
|
||||||
|
* OK response payload:
|
||||||
|
* <[8] 64-bit network ID>
|
||||||
|
* <[...] network configuration dictionary>
|
||||||
|
*
|
||||||
|
* OK returns a Dictionary (string serialized) containing the network's
|
||||||
|
* configuration and IP address assignment information for the querying
|
||||||
|
* node.
|
||||||
|
*
|
||||||
|
* ERROR may be NOT_FOUND if no such network is known, or
|
||||||
|
* UNSUPPORTED_OPERATION if the netconf service isn't available. The
|
||||||
|
* payload will be the network ID.
|
||||||
|
*/
|
||||||
|
VERB_NETWORK_CONFIG_REQUEST = 11,
|
||||||
|
|
||||||
|
/* Network configuration refresh request:
|
||||||
|
* <[8] 64-bit network ID>
|
||||||
|
*
|
||||||
|
* This message can be sent by the network configuration master node
|
||||||
|
* to request that nodes refresh their network configuration.
|
||||||
|
*
|
||||||
|
* It is only a hint and does not presently elicit a response.
|
||||||
|
*/
|
||||||
|
VERB_NETWORK_CONFIG_REFRESH = 12
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -508,10 +540,7 @@ public:
|
||||||
ERROR_UNSUPPORTED_OPERATION = 6,
|
ERROR_UNSUPPORTED_OPERATION = 6,
|
||||||
|
|
||||||
/* Message to private network rejected -- no unexpired certificate on file */
|
/* Message to private network rejected -- no unexpired certificate on file */
|
||||||
ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7,
|
ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7
|
||||||
|
|
||||||
/* Object is expired (e.g. network certificate) */
|
|
||||||
ERROR_OBJECT_EXPIRED = 8
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -624,14 +653,14 @@ public:
|
||||||
*
|
*
|
||||||
* @return Destination ZT address
|
* @return Destination ZT address
|
||||||
*/
|
*/
|
||||||
inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH)); }
|
inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this packet's source
|
* Get this packet's source
|
||||||
*
|
*
|
||||||
* @return Source ZT address
|
* @return Source ZT address
|
||||||
*/
|
*/
|
||||||
inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH)); }
|
inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if packet is of valid length
|
* @return True if packet is of valid length
|
||||||
|
|
|
@ -102,6 +102,12 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
|
||||||
return _doMULTICAST_LIKE(_r,peer);
|
return _doMULTICAST_LIKE(_r,peer);
|
||||||
case Packet::VERB_MULTICAST_FRAME:
|
case Packet::VERB_MULTICAST_FRAME:
|
||||||
return _doMULTICAST_FRAME(_r,peer);
|
return _doMULTICAST_FRAME(_r,peer);
|
||||||
|
case Packet::VERB_NETWORK_PERMISSION_CERTIFICATE:
|
||||||
|
return _doNETWORK_PERMISSION_CERTIFICATE(_r,peer);
|
||||||
|
case Packet::VERB_NETWORK_CONFIG_REQUEST:
|
||||||
|
return _doNETWORK_CONFIG_REQUEST(_r,peer);
|
||||||
|
case Packet::VERB_NETWORK_CONFIG_REFRESH:
|
||||||
|
return _doNETWORK_CONFIG_REFRESH(_r,peer);
|
||||||
default:
|
default:
|
||||||
// This might be something from a new or old version of the protocol.
|
// This might be something from a new or old version of the protocol.
|
||||||
// Technically it passed HMAC so the packet is still valid, but we
|
// Technically it passed HMAC so the packet is still valid, but we
|
||||||
|
@ -538,4 +544,16 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PacketDecoder::_doNETWORK_PERMISSION_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -122,6 +122,9 @@ private:
|
||||||
bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
||||||
bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
||||||
bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
||||||
|
bool _doNETWORK_PERMISSION_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
||||||
|
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
||||||
|
bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
|
||||||
|
|
||||||
uint64_t _receiveTime;
|
uint64_t _receiveTime;
|
||||||
Demarc::Port _localPort;
|
Demarc::Port _localPort;
|
||||||
|
|
Loading…
Add table
Reference in a new issue