summaryrefslogtreecommitdiff
path: root/zerotierone/node
diff options
context:
space:
mode:
Diffstat (limited to 'zerotierone/node')
-rw-r--r--zerotierone/node/CertificateOfMembership.hpp1
-rw-r--r--zerotierone/node/Dictionary.cpp245
-rw-r--r--zerotierone/node/Dictionary.hpp510
-rw-r--r--zerotierone/node/IncomingPacket.cpp100
-rw-r--r--zerotierone/node/IncomingPacket.hpp8
-rw-r--r--zerotierone/node/InetAddress.cpp25
-rw-r--r--zerotierone/node/InetAddress.hpp31
-rw-r--r--zerotierone/node/Multicaster.cpp4
-rw-r--r--zerotierone/node/Network.cpp106
-rw-r--r--zerotierone/node/Network.hpp16
-rw-r--r--zerotierone/node/NetworkConfig.cpp563
-rw-r--r--zerotierone/node/NetworkConfig.hpp407
-rw-r--r--zerotierone/node/NetworkConfigRequestMetaData.hpp196
-rw-r--r--zerotierone/node/NetworkController.hpp18
-rw-r--r--zerotierone/node/Node.cpp13
-rw-r--r--zerotierone/node/Node.hpp13
-rw-r--r--zerotierone/node/Packet.hpp57
-rw-r--r--zerotierone/node/Path.hpp2
-rw-r--r--zerotierone/node/Switch.cpp126
-rw-r--r--zerotierone/node/Topology.cpp1
-rw-r--r--zerotierone/node/Topology.hpp53
-rw-r--r--zerotierone/node/Utils.cpp18
-rw-r--r--zerotierone/node/Utils.hpp29
23 files changed, 1279 insertions, 1263 deletions
diff --git a/zerotierone/node/CertificateOfMembership.hpp b/zerotierone/node/CertificateOfMembership.hpp
index 43c8c0a..0342bc3 100644
--- a/zerotierone/node/CertificateOfMembership.hpp
+++ b/zerotierone/node/CertificateOfMembership.hpp
@@ -295,7 +295,6 @@ public:
* @param s String to deserialize
*/
void fromString(const char *s);
- inline void fromString(const std::string &s) { fromString(s.c_str()); }
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
/**
diff --git a/zerotierone/node/Dictionary.cpp b/zerotierone/node/Dictionary.cpp
deleted file mode 100644
index b334066..0000000
--- a/zerotierone/node/Dictionary.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "Dictionary.hpp"
-
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
-
-#include "C25519.hpp"
-#include "Identity.hpp"
-#include "Utils.hpp"
-
-namespace ZeroTier {
-
-Dictionary::iterator Dictionary::find(const std::string &key)
-{
- for(iterator i(begin());i!=end();++i) {
- if (i->first == key)
- return i;
- }
- return end();
-}
-Dictionary::const_iterator Dictionary::find(const std::string &key) const
-{
- for(const_iterator i(begin());i!=end();++i) {
- if (i->first == key)
- return i;
- }
- return end();
-}
-
-bool Dictionary::getBoolean(const std::string &key,bool dfl) const
-{
- const_iterator e(find(key));
- if (e == end())
- return dfl;
- if (e->second.length() < 1)
- return dfl;
- switch(e->second[0]) {
- case '1':
- case 't':
- case 'T':
- case 'y':
- case 'Y':
- return true;
- }
- return false;
-}
-
-std::string &Dictionary::operator[](const std::string &key)
-{
- for(iterator i(begin());i!=end();++i) {
- if (i->first == key)
- return i->second;
- }
- push_back(std::pair<std::string,std::string>(key,std::string()));
- std::sort(begin(),end());
- for(iterator i(begin());i!=end();++i) {
- if (i->first == key)
- return i->second;
- }
- return front().second; // should be unreachable!
-}
-
-std::string Dictionary::toString() const
-{
- std::string s;
- for(const_iterator kv(begin());kv!=end();++kv) {
- _appendEsc(kv->first.data(),(unsigned int)kv->first.length(),s);
- s.push_back('=');
- _appendEsc(kv->second.data(),(unsigned int)kv->second.length(),s);
- s.append(ZT_EOL_S);
- }
- return s;
-}
-
-void Dictionary::updateFromString(const char *s,unsigned int maxlen)
-{
- bool escapeState = false;
- std::string keyBuf;
- std::string *element = &keyBuf;
- const char *end = s + maxlen;
- while ((*s)&&(s < end)) {
- if (escapeState) {
- escapeState = false;
- switch(*s) {
- case '0':
- element->push_back((char)0);
- break;
- case 'r':
- element->push_back('\r');
- break;
- case 'n':
- element->push_back('\n');
- break;
- default:
- element->push_back(*s);
- break;
- }
- } else {
- if (*s == '\\') {
- escapeState = true;
- } else if (*s == '=') {
- if (element == &keyBuf)
- element = &((*this)[keyBuf]);
- } else if ((*s == '\r')||(*s == '\n')) {
- if ((element == &keyBuf)&&(keyBuf.length() > 0))
- (*this)[keyBuf];
- keyBuf = "";
- element = &keyBuf;
- } else element->push_back(*s);
- }
- ++s;
- }
- if ((element == &keyBuf)&&(keyBuf.length() > 0))
- (*this)[keyBuf];
-}
-
-void Dictionary::fromString(const char *s,unsigned int maxlen)
-{
- clear();
- updateFromString(s,maxlen);
-}
-
-void Dictionary::eraseKey(const std::string &key)
-{
- for(iterator i(begin());i!=end();++i) {
- if (i->first == key) {
- this->erase(i);
- return;
- }
- }
-}
-
-bool Dictionary::sign(const Identity &id,uint64_t now)
-{
- try {
- // Sign identity and timestamp fields too. If there's an existing
- // signature, _mkSigBuf() ignores it.
- char nows[32];
- Utils::snprintf(nows,sizeof(nows),"%llx",(unsigned long long)now);
- (*this)[ZT_DICTIONARY_SIGNATURE_IDENTITY] = id.toString(false);
- (*this)[ZT_DICTIONARY_SIGNATURE_TIMESTAMP] = nows;
-
- // Create a blob to hash and sign from fields in sorted order
- std::string buf;
- _mkSigBuf(buf);
-
- // Add signature field
- C25519::Signature sig(id.sign(buf.data(),(unsigned int)buf.length()));
- (*this)[ZT_DICTIONARY_SIGNATURE] = Utils::hex(sig.data,(unsigned int)sig.size());
-
- return true;
- } catch ( ... ) {
- // Probably means identity has no secret key field
- removeSignature();
- return false;
- }
-}
-
-bool Dictionary::verify(const Identity &id) const
-{
- try {
- std::string buf;
- _mkSigBuf(buf);
- const_iterator sig(find(ZT_DICTIONARY_SIGNATURE));
- if (sig == end())
- return false;
- std::string sigbin(Utils::unhex(sig->second));
- return id.verify(buf.data(),(unsigned int)buf.length(),sigbin.data(),(unsigned int)sigbin.length());
- } catch ( ... ) {
- return false;
- }
-}
-
-uint64_t Dictionary::signatureTimestamp() const
-{
- const_iterator ts(find(ZT_DICTIONARY_SIGNATURE_TIMESTAMP));
- if (ts == end())
- return 0;
- return Utils::hexStrToU64(ts->second.c_str());
-}
-
-void Dictionary::_mkSigBuf(std::string &buf) const
-{
- unsigned long pairs = 0;
- for(const_iterator i(begin());i!=end();++i) {
- if (i->first != ZT_DICTIONARY_SIGNATURE) {
- buf.append(i->first);
- buf.push_back('=');
- buf.append(i->second);
- buf.push_back('\0');
- ++pairs;
- }
- }
- buf.push_back((char)0xff);
- buf.push_back((char)((pairs >> 24) & 0xff)); // pad with number of key/value pairs at end
- buf.push_back((char)((pairs >> 16) & 0xff));
- buf.push_back((char)((pairs >> 8) & 0xff));
- buf.push_back((char)(pairs & 0xff));
-}
-
-void Dictionary::_appendEsc(const char *data,unsigned int len,std::string &to)
-{
- for(unsigned int i=0;i<len;++i) {
- switch(data[i]) {
- case 0:
- to.append("\\0");
- break;
- case '\r':
- to.append("\\r");
- break;
- case '\n':
- to.append("\\n");
- break;
- case '\\':
- to.append("\\\\");
- break;
- case '=':
- to.append("\\=");
- break;
- default:
- to.push_back(data[i]);
- break;
- }
- }
-}
-
-} // namespace ZeroTier
-
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
diff --git a/zerotierone/node/Dictionary.hpp b/zerotierone/node/Dictionary.hpp
index d4cdd23..59fc4bb 100644
--- a/zerotierone/node/Dictionary.hpp
+++ b/zerotierone/node/Dictionary.hpp
@@ -20,261 +20,443 @@
#define ZT_DICTIONARY_HPP
#include "Constants.hpp"
-
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-#include <stdexcept>
-#include <algorithm>
-
#include "Utils.hpp"
+#include "Buffer.hpp"
+#include "Address.hpp"
-// Three fields are added/updated by sign()
-#define ZT_DICTIONARY_SIGNATURE "~!ed25519"
-#define ZT_DICTIONARY_SIGNATURE_IDENTITY "~!sigid"
-#define ZT_DICTIONARY_SIGNATURE_TIMESTAMP "~!sigts"
+#include <stdint.h>
namespace ZeroTier {
-class Identity;
-
/**
- * Simple key/value dictionary with string serialization
+ * A small (in code and data) packed key=value store
+ *
+ * This stores data in the form of a compact blob that is sort of human
+ * readable (depending on whether you put binary data in it) and is backward
+ * compatible with older versions. Binary data is escaped such that the
+ * serialized form of a Dictionary is always a valid null-terminated C string.
+ *
+ * Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key
+ * contains these characters it may not be retrievable. This is not checked.
+ *
+ * Lookup is via linear search and will be slow with a lot of keys. It's
+ * designed for small things.
*
- * The serialization format is a flat key=value with backslash escape.
- * 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.
+ * There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of
+ * pointer tricks like this is important after any modifications.
*
- * Keys beginning with "~!" are reserved for signature data fields.
+ * This is used for network configurations and for saving some things on disk
+ * in the ZeroTier One service code.
*
- * It's stored as a simple vector and can be linearly scanned or
- * binary searched. Dictionaries are only used for very small things
- * outside the core loop, so this is not a significant performance
- * issue and it reduces memory use and code footprint.
+ * @tparam C Dictionary max capacity in bytes
*/
-class Dictionary : public std::vector< std::pair<std::string,std::string> >
+template<unsigned int C>
+class Dictionary
{
public:
- Dictionary() {}
+ Dictionary()
+ {
+ _d[0] = (char)0;
+ }
- /**
- * @param s String-serialized dictionary
- * @param maxlen Maximum length of buffer
- */
- Dictionary(const char *s,unsigned int maxlen) { fromString(s,maxlen); }
+ Dictionary(const char *s)
+ {
+ Utils::scopy(_d,sizeof(_d),s);
+ }
- /**
- * @param s String-serialized dictionary
- */
- Dictionary(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); }
+ Dictionary(const char *s,unsigned int len)
+ {
+ if (len > (C-1))
+ len = C-1;
+ memcpy(_d,s,len);
+ _d[len] = (char)0;
+ }
- iterator find(const std::string &key);
- const_iterator find(const std::string &key) const;
+ Dictionary(const Dictionary &d)
+ {
+ Utils::scopy(_d,sizeof(_d),d._d);
+ }
- /**
- * 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
+ inline Dictionary &operator=(const Dictionary &d)
{
- const_iterator e(find(key));
- if (e == end())
- return dfl;
- return e->second;
+ Utils::scopy(_d,sizeof(_d),d._d);
+ return *this;
}
/**
- * @param key Key to get
- * @param dfl Default boolean result if key not found or empty (default: false)
- * @return Boolean value of key
+ * Load a dictionary from a C-string
+ *
+ * @param s Dictionary in string form
+ * @return False if 's' was longer than our capacity
*/
- bool getBoolean(const std::string &key,bool dfl = false) const;
+ inline bool load(const char *s)
+ {
+ return Utils::scopy(_d,sizeof(_d),s);
+ }
/**
- * @param key Key to get
- * @param dfl Default value if not present (default: 0)
- * @return Value converted to unsigned 64-bit int or 0 if not found
+ * Delete all entries
*/
- inline uint64_t getUInt(const std::string &key,uint64_t dfl = 0) const
+ inline void clear()
{
- const_iterator e(find(key));
- if (e == end())
- return dfl;
- return Utils::strToU64(e->second.c_str());
+ _d[0] = (char)0;
}
/**
- * @param key Key to get
- * @param dfl Default value if not present (default: 0)
- * @return Value converted to unsigned 64-bit int or 0 if not found
+ * @return Size of dictionary in bytes not including terminating NULL
*/
- inline uint64_t getHexUInt(const std::string &key,uint64_t dfl = 0) const
+ inline unsigned int sizeBytes() const
{
- const_iterator e(find(key));
- if (e == end())
- return dfl;
- return Utils::hexStrToU64(e->second.c_str());
+ for(unsigned int i=0;i<C;++i) {
+ if (!_d[i])
+ return i;
+ }
+ return C-1;
}
/**
- * @param key Key to get
- * @param dfl Default value if not present (default: 0)
- * @return Value converted to signed 64-bit int or 0 if not found
+ * Get an entry
+ *
+ * Note that to get binary values, dest[] should be at least one more than
+ * the maximum size of the value being retrieved. That's because even if
+ * the data is binary a terminating 0 is still appended to dest[] after it.
+ *
+ * If the key is not found, dest[0] is set to 0 to make dest[] an empty
+ * C string in that case. The dest[] array will *never* be unterminated
+ * after this call.
+ *
+ * Security note: if 'key' is ever directly based on anything that is not
+ * a hard-code or internally-generated name, it must be checked to ensure
+ * that the buffer is NULL-terminated since key[] does not take a secondary
+ * size parameter. In NetworkConfig all keys are hard-coded strings so this
+ * isn't a problem in the core.
+ *
+ * @param key Key to look up
+ * @param dest Destination buffer
+ * @param destlen Size of destination buffer
+ * @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
*/
- inline int64_t getInt(const std::string &key,int64_t dfl = 0) const
+ inline int get(const char *key,char *dest,unsigned int destlen) const
{
- const_iterator e(find(key));
- if (e == end())
- return dfl;
- return Utils::strTo64(e->second.c_str());
+ const char *p = _d;
+ const char *const eof = p + C;
+ const char *k;
+ bool esc;
+ int j;
+
+ if (!destlen) // sanity check
+ return -1;
+
+ while (*p) {
+ k = key;
+ while ((*k)&&(*p)) {
+ if (*p != *k)
+ break;
+ ++k;
+ if (++p == eof) {
+ dest[0] = (char)0;
+ return -1;
+ }
+ }
+
+ if ((!*k)&&(*p == '=')) {
+ j = 0;
+ esc = false;
+ ++p;
+ while ((*p != 0)&&(*p != '\r')&&(*p != '\n')) {
+ if (esc) {
+ esc = false;
+ switch(*p) {
+ case 'r': dest[j++] = '\r'; break;
+ case 'n': dest[j++] = '\n'; break;
+ case '0': dest[j++] = (char)0; break;
+ case 'e': dest[j++] = '='; break;
+ default: dest[j++] = *p; break;
+ }
+ if (j == (int)destlen) {
+ dest[j-1] = (char)0;
+ return j-1;
+ }
+ } else if (*p == '\\') {
+ esc = true;
+ } else {
+ dest[j++] = *p;
+ if (j == (int)destlen) {
+ dest[j-1] = (char)0;
+ return j-1;
+ }
+ }
+ if (++p == eof) {
+ dest[0] = (char)0;
+ return -1;
+ }
+ }
+ dest[j] = (char)0;
+ return j;
+ } else {
+ while ((*p)&&(*p != '\r')&&(*p != '\n')) {
+ if (++p == eof) {
+ dest[0] = (char)0;
+ return -1;
+ }
+ }
+ if (*p) {
+ if (++p == eof) {
+ dest[0] = (char)0;
+ return -1;
+ }
+ }
+ else break;
+ }
+ }
+
+ dest[0] = (char)0;
+ return -1;
}
- std::string &operator[](const std::string &key);
-
/**
- * @param key Key to set
- * @param value String value
+ * Get the contents of a key into a buffer
+ *
+ * @param key Key to get
+ * @param dest Destination buffer
+ * @return True if key was found (if false, dest will be empty)
+ * @tparam BC Buffer capacity (usually inferred)
*/
- inline void set(const std::string &key,const char *value)
+ template<unsigned int BC>
+ inline bool get(const char *key,Buffer<BC> &dest) const
{
- (*this)[key] = value;
+ const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
+ if (r >= 0) {
+ dest.setSize((unsigned int)r);
+ return true;
+ } else {
+ dest.clear();
+ return false;
+ }
}
/**
- * @param key Key to set
- * @param value String value
+ * Get a boolean value
+ *
+ * @param key Key to look up
+ * @param dfl Default value if not found in dictionary
+ * @return Boolean value of key or 'dfl' if not found
*/
- inline void set(const std::string &key,const std::string &value)
+ bool getB(const char *key,bool dfl = false) const
{
- (*this)[key] = value;
+ char tmp[4];
+ if (this->get(key,tmp,sizeof(tmp)) >= 0)
+ return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
+ return dfl;
}
/**
- * @param key Key to set
- * @param value Boolean value
+ * Get an unsigned int64 stored as hex in the dictionary
+ *
+ * @param key Key to look up
+ * @param dfl Default value or 0 if unspecified
+ * @return Decoded hex UInt value or 'dfl' if not found
*/
- inline void set(const std::string &key,bool value)
+ inline uint64_t getUI(const char *key,uint64_t dfl = 0) const
{
- (*this)[key] = ((value) ? "1" : "0");
+ char tmp[128];
+ if (this->get(key,tmp,sizeof(tmp)) >= 1)
+ return Utils::hexStrToU64(tmp);
+ return dfl;
}
/**
- * @param key Key to set
- * @param value Integer value
+ * Add a new key=value pair
+ *
+ * If the key is already present this will append another, but the first
+ * will always be returned by get(). This is not checked. If you want to
+ * ensure a key is not present use erase() first.
+ *
+ * Use the vlen parameter to add binary values. Nulls will be escaped.
+ *
+ * @param key Key -- nulls, CR/LF, and equals (=) are illegal characters
+ * @param value Value to set
+ * @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
+ * @return True if there was enough room to add this key=value pair
*/
- inline void set(const std::string &key,uint64_t value)
+ inline bool add(const char *key,const char *value,int vlen = -1)
{
- char tmp[24];
- Utils::snprintf(tmp,sizeof(tmp),"%llu",(unsigned long long)value);
- (*this)[key] = tmp;
+ for(unsigned int i=0;i<C;++i) {
+ if (!_d[i]) {
+ unsigned int j = i;
+
+ if (j > 0) {
+ _d[j++] = '\n';
+ if (j == C) {
+ _d[i] = (char)0;
+ return false;
+ }
+ }
+
+ const char *p = key;
+ while (*p) {
+ _d[j++] = *(p++);
+ if (j == C) {
+ _d[i] = (char)0;
+ return false;
+ }
+ }
+
+ _d[j++] = '=';
+ if (j == C) {
+ _d[i] = (char)0;
+ return false;
+ }
+
+ p = value;
+ int k = 0;
+ while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
+ switch(*p) {
+ case 0:
+ case '\r':
+ case '\n':
+ case '\\':
+ case '=':
+ _d[j++] = '\\';
+ if (j == C) {
+ _d[i] = (char)0;
+ return false;
+ }
+ switch(*p) {
+ case 0: _d[j++] = '0'; break;
+ case '\r': _d[j++] = 'r'; break;
+ case '\n': _d[j++] = 'n'; break;
+ case '\\': _d[j++] = '\\'; break;
+ case '=': _d[j++] = 'e'; break;
+ }
+ if (j == C) {
+ _d[i] = (char)0;
+ return false;
+ }
+ break;
+ default:
+ _d[j++] = *p;
+ if (j == C) {
+ _d[i] = (char)0;
+ return false;
+ }
+ break;
+ }
+ ++p;
+ ++k;
+ }
+
+ _d[j] = (char)0;
+
+ return true;
+ }
+ }
+ return false;
}
/**
- * @param key Key to set
- * @param value Integer value
+ * Add a boolean as a '1' or a '0'
*/
- inline void set(const std::string &key,int64_t value)
+ inline bool add(const char *key,bool value)
{
- char tmp[24];
- Utils::snprintf(tmp,sizeof(tmp),"%lld",(long long)value);
- (*this)[key] = tmp;
+ return this->add(key,(value) ? "1" : "0",1);
}
- /**
- * @param key Key to set
- * @param value Integer value
+ /**
+ * Add a 64-bit integer (unsigned) as a hex value
*/
- inline void setHex(const std::string &key,uint64_t value)
+ inline bool add(const char *key,uint64_t value)
{
- char tmp[24];
+ char tmp[32];
Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
- (*this)[key] = tmp;
+ return this->add(key,tmp,-1);
}
- /**
- * @param key Key to check
- * @return True if dictionary contains key
+ /**
+ * Add a 64-bit integer (unsigned) as a hex value
*/
- inline bool contains(const std::string &key) const { return (find(key) != end()); }
-
- /**
- * @return String-serialized dictionary
- */
- std::string toString() const;
+ inline bool add(const char *key,const Address &a)
+ {
+ char tmp[32];
+ Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
+ return this->add(key,tmp,-1);
+ }
/**
- * Clear and initialize from a string
+ * Add a binary buffer's contents as a value
*
- * @param s String-serialized dictionary
- * @param maxlen Maximum length of string buffer
+ * @tparam BC Buffer capacity (usually inferred)
*/
- void fromString(const char *s,unsigned int maxlen);
- inline void fromString(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); }
- void updateFromString(const char *s,unsigned int maxlen);
- inline void update(const char *s,unsigned int maxlen) { updateFromString(s, maxlen); }
- inline void update(const std::string &s) { updateFromString(s.c_str(),(unsigned int)s.length()); }
-
- /**
- * @return True if this dictionary is cryptographically signed
- */
- inline bool hasSignature() const { return (find(ZT_DICTIONARY_SIGNATURE) != end()); }
-
- /**
- * @return Signing identity in string-serialized format or empty string if none
- */
- inline std::string signingIdentity() const { return get(ZT_DICTIONARY_SIGNATURE_IDENTITY,std::string()); }
+ template<unsigned int BC>
+ inline bool add(const char *key,const Buffer<BC> &value)
+ {
+ return this->add(key,(const char *)value.data(),(int)value.size());
+ }
/**
- * @return Signature timestamp in milliseconds since epoch or 0 if none
+ * @param key Key to check
+ * @return True if key is present
*/
- uint64_t signatureTimestamp() const;
+ inline bool contains(const char *key) const
+ {
+ char tmp[2];
+ return (this->get(key,tmp,2) >= 0);
+ }
/**
+ * Erase a key from this dictionary
+ *
+ * Use this before add() to ensure that a key is replaced if it might
+ * already be present.
+ *
* @param key Key to erase
+ * @return True if key was found and erased
*/
- void eraseKey(const std::string &key);
-
- /**
- * Remove any signature from this dictionary
- */
- inline void removeSignature()
+ inline bool erase(const char *key)
{
- eraseKey(ZT_DICTIONARY_SIGNATURE);
- eraseKey(ZT_DICTIONARY_SIGNATURE_IDENTITY);
- eraseKey(ZT_DICTIONARY_SIGNATURE_TIMESTAMP);
+ char d2[C];
+ char *saveptr = (char *)0;
+ unsigned int d2ptr = 0;
+ bool found = false;
+ for(char *f=Utils::stok(_d,"\r\n",&saveptr);(f);f=Utils::stok((char *)0,"\r\n",&saveptr)) {
+ if (*f) {
+ const char *p = f;
+ const char *k = key;
+ while ((*k)&&(*p)) {
+ if (*k != *p)
+ break;
+ ++k;
+ ++p;
+ }
+ if (*k) {
+ p = f;
+ while (*p)
+ d2[d2ptr++] = *(p++);
+ d2[d2ptr++] = '\n';
+ } else {
+ found = true;
+ }
+ }
+ }
+ d2[d2ptr++] = (char)0;
+ memcpy(_d,d2,d2ptr);
+ return found;
}
/**
- * Add or update signature fields with a signature of all other keys and values
- *
- * @param with Identity to sign with (must have secret key)
- * @param now Current time
- * @return True on success
+ * @return Dictionary data as a 0-terminated C-string
*/
- bool sign(const Identity &id,uint64_t now);
+ inline const char *data() const { return _d; }
/**
- * Verify signature against an identity
- *
- * @param id Identity to verify against
- * @return True if signature verification OK
+ * @return Value of C template parameter
*/
- bool verify(const Identity &id) const;
+ inline unsigned int capacity() const { return C; }
private:
- void _mkSigBuf(std::string &buf) const;
- static void _appendEsc(const char *data,unsigned int len,std::string &to);
+ char _d[C];
};
} // namespace ZeroTier
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
-
#endif
diff --git a/zerotierone/node/IncomingPacket.cpp b/zerotierone/node/IncomingPacket.cpp
index 9e0226b..37af842 100644
--- a/zerotierone/node/IncomingPacket.cpp
+++ b/zerotierone/node/IncomingPacket.cpp
@@ -43,8 +43,23 @@ namespace ZeroTier {
bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
{
const Address sourceAddress(source());
+
try {
- if ((cipher() == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
+ // Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
+ const unsigned int c = cipher();
+ bool trusted = false;
+ if (c == ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH) {
+ // If this is marked as a packet via a trusted path, check source address and path ID.
+ // Obviously if no trusted paths are configured this always returns false and such
+ // packets are dropped on the floor.
+ if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
+ trusted = true;
+ TRACE("TRUSTED PATH packet approved from %s(%s), trusted path ID %llx",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
+ } else {
+ TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str());
+ return true;
+ }
+ } else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
// Unencrypted HELLOs require some potentially expensive verification, so
// do this in the background if background processing is enabled.
if ((RR->dpEnabled > 0)&&(!deferred)) {
@@ -61,12 +76,15 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
if (peer) {
- if (!dearmor(peer->key())) {
- TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size());
- return true;
+ if (!trusted) {
+ if (!dearmor(peer->key())) {
+ TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),size());
+ return true;
+ }
}
+
if (!uncompress()) {
- TRACE("dropped packet from %s(%s), compressed data invalid",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ TRACE("dropped packet from %s(%s), compressed data invalid",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
return true;
}
@@ -403,8 +421,12 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
if ((nw)&&(nw->controller() == peer->address())) {
const unsigned int nclen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
if (nclen) {
- nw->setConfiguration(field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,nclen),nclen,true);
- TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str());
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,nclen),nclen);
+ NetworkConfig nconf;
+ if (nconf.fromDictionary(dconf)) {
+ nw->setConfiguration(nconf,true);
+ TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str());
+ }
}
}
} break;
@@ -679,27 +701,8 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
- const uint8_t *metaDataBytes = (const uint8_t *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
-
- NetworkConfigRequestMetaData metaData;
- bool haveNewStyleMetaData = false;
- for(unsigned int i=0;i<metaDataLength;++i) {
- if ((metaDataBytes[i] == 0)&&(i < (metaDataLength - 2))) {
- haveNewStyleMetaData = true;
- break;
- }
- }
- if (haveNewStyleMetaData) {
- Buffer<4096> md(metaDataBytes,metaDataLength);
- metaData.deserialize(md,0); // the meta-data deserializer automatically skips old-style meta-data
- } else {
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- const Dictionary oldStyleMetaData((const char *)metaDataBytes,metaDataLength);
- metaData.majorVersion = (unsigned int)oldStyleMetaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0);
- metaData.minorVersion = (unsigned int)oldStyleMetaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0);
- metaData.revision = (unsigned int)oldStyleMetaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0);
-#endif
- }
+ const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
+ const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
//const uint64_t haveRevision = ((ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength + 8) <= size()) ? at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength) : 0ULL;
@@ -708,22 +711,21 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
peer->received(_localAddress,_remoteAddress,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP);
if (RR->localNetworkController) {
- Buffer<8194> netconf;
+ NetworkConfig netconf;
switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,RR->identity,peer->identity(),nwid,metaData,netconf)) {
case NetworkController::NETCONF_QUERY_OK: {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append(nwid);
- outp.append((uint16_t)netconf.size());
- outp.append(netconf.data(),(unsigned int)netconf.size());
- outp.compress();
- outp.armor(peer->key(),true);
- if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check
- //TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length());
- } else {
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf;
+ if (netconf.toDictionary(dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(pid);
+ outp.append(nwid);
+ const unsigned int dlen = dconf.sizeBytes();
+ outp.append((uint16_t)dlen);
+ outp.append((const void *)dconf.data(),dlen);
+ outp.compress();
+ RR->sw->send(outp,true,0);
}
} break;
@@ -1208,8 +1210,20 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S
bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
{
try {
- // Right now this is only allowed from root servers -- may be allowed from controllers and relays later.
- if (RR->topology->isRoot(peer->identity())) {
+ // If this were allowed from anyone, it would itself be a DOS vector. Right
+ // now we only allow it from roots and controllers of networks you have joined.
+ bool allowed = RR->topology->isRoot(peer->identity());
+ if (!allowed) {
+ std::vector< SharedPtr<Network> > allNetworks(RR->node->allNetworks());
+ for(std::vector< SharedPtr<Network> >::const_iterator n(allNetworks.begin());n!=allNetworks.end();++n) {
+ if (peer->address() == (*n)->controller()) {
+ allowed = true;
+ break;
+ }
+ }
+ }
+
+ if (allowed) {
const uint64_t pid = packetId();
const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1];
const unsigned int challengeLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 2);
diff --git a/zerotierone/node/IncomingPacket.hpp b/zerotierone/node/IncomingPacket.hpp
index 96e46c0..cd0b7dc 100644
--- a/zerotierone/node/IncomingPacket.hpp
+++ b/zerotierone/node/IncomingPacket.hpp
@@ -79,10 +79,10 @@ public:
* @throws std::out_of_range Range error processing packet
*/
IncomingPacket(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now) :
- Packet(data,len),
- _receiveTime(now),
- _localAddress(localAddress),
- _remoteAddress(remoteAddress)
+ Packet(data,len),
+ _receiveTime(now),
+ _localAddress(localAddress),
+ _remoteAddress(remoteAddress)
{
}
diff --git a/zerotierone/node/InetAddress.cpp b/zerotierone/node/InetAddress.cpp
index dca772e..3f6b9be 100644
--- a/zerotierone/node/InetAddress.cpp
+++ b/zerotierone/node/InetAddress.cpp
@@ -279,6 +279,8 @@ bool InetAddress::containsAddress(const InetAddress &addr) const
switch(ss_family) {
case AF_INET: {
const unsigned int bits = netmaskBits();
+ if (bits == 0)
+ return true;
return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) );
}
case AF_INET6: {
@@ -393,7 +395,6 @@ bool InetAddress::operator<(const InetAddress &a) const
}
InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
- throw()
{
struct sockaddr_in6 sin6;
sin6.sin6_family = AF_INET6;
@@ -418,7 +419,6 @@ InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
}
InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
- throw()
{
InetAddress r;
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
@@ -443,4 +443,25 @@ InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
return r;
}
+InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
+{
+ nwid ^= (nwid >> 32);
+ InetAddress r;
+ struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr.s6_addr[0] = 0xfc;
+ sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24);
+ sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16);
+ sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8);
+ sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
+ sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32);
+ sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24);
+ sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16);
+ sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8);
+ sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
+ sin6->sin6_addr.s6_addr[15] = 0x01;
+ sin6->sin6_port = Utils::hton((uint16_t)40);
+ return r;
+}
+
} // namespace ZeroTier
diff --git a/zerotierone/node/InetAddress.hpp b/zerotierone/node/InetAddress.hpp
index b60a5a3..e03deb7 100644
--- a/zerotierone/node/InetAddress.hpp
+++ b/zerotierone/node/InetAddress.hpp
@@ -231,7 +231,6 @@ struct InetAddress : public sockaddr_storage
* @param port Port, 0 to 65535
*/
inline void setPort(unsigned int port)
- throw()
{
switch(ss_family) {
case AF_INET:
@@ -244,6 +243,25 @@ struct InetAddress : public sockaddr_storage
}
/**
+ * @return True if this network/netmask route describes a default route (e.g. 0.0.0.0/0)
+ */
+ inline bool isDefaultRoute() const
+ {
+ switch(ss_family) {
+ case AF_INET:
+ return ( (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == 0) && (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == 0) );
+ case AF_INET6:
+ const uint8_t *ipb = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(int i=0;i<16;++i) {
+ if (ipb[i])
+ return false;
+ }
+ return (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == 0);
+ }
+ return false;
+ }
+
+ /**
* @return ASCII IP/port format representation
*/
std::string toString() const;
@@ -453,8 +471,7 @@ struct InetAddress : public sockaddr_storage
* @param mac MAC address seed
* @return IPv6 link-local address
*/
- static InetAddress makeIpv6LinkLocal(const MAC &mac)
- throw();
+ static InetAddress makeIpv6LinkLocal(const MAC &mac);
/**
* Compute private IPv6 unicast address from network ID and ZeroTier address
@@ -497,8 +514,12 @@ struct InetAddress : public sockaddr_storage
* @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
* @return IPv6 private unicast address with /88 netmask
*/
- static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
- throw();
+ static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress);
+
+ /**
+ * Compute a private IPv6 "6plane" unicast address from network ID and ZeroTier address
+ */
+ static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
};
} // namespace ZeroTier
diff --git a/zerotierone/node/Multicaster.cpp b/zerotierone/node/Multicaster.cpp
index 1dfa67d..e1d4567 100644
--- a/zerotierone/node/Multicaster.cpp
+++ b/zerotierone/node/Multicaster.cpp
@@ -228,7 +228,9 @@ void Multicaster::send(
gs.lastExplicitGather = now;
SharedPtr<Peer> explicitGatherPeers[2];
explicitGatherPeers[0] = RR->topology->getBestRoot();
- explicitGatherPeers[1] = RR->topology->getPeer(Network::controllerFor(nwid));
+ const Address nwidc(Network::controllerFor(nwid));
+ if (nwidc != RR->identity.address())
+ explicitGatherPeers[1] = RR->topology->getPeer(nwidc);
for(unsigned int k=0;k<2;++k) {
const SharedPtr<Peer> &p = explicitGatherPeers[k];
if (!p)
diff --git a/zerotierone/node/Network.cpp b/zerotierone/node/Network.cpp
index c1c3414..2511664 100644
--- a/zerotierone/node/Network.cpp
+++ b/zerotierone/node/Network.cpp
@@ -41,7 +41,6 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
_uPtr(uptr),
_id(nwid),
_mac(renv->identity.address(),nwid),
- _enabled(true),
_portInitialized(false),
_lastConfigUpdate(0),
_destroyed(false),
@@ -65,9 +64,13 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
try {
std::string conf(RR->node->dataStoreGet(confn));
if (conf.length()) {
- this->setConfiguration((const void *)conf.data(),(unsigned int)conf.length(),false);
- _lastConfigUpdate = 0; // we still want to re-request a new config from the network
- gotConf = true;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf(conf.c_str());
+ NetworkConfig nconf;
+ if (nconf.fromDictionary(dconf)) {
+ this->setConfiguration(nconf,false);
+ _lastConfigUpdate = 0; // we still want to re-request a new config from the network
+ gotConf = true;
+ }
}
} catch ( ... ) {} // ignore invalids, we'll re-request
@@ -178,49 +181,21 @@ bool Network::applyConfiguration(const NetworkConfig &conf)
return false;
}
-int Network::setConfiguration(const void *confBytes,unsigned int confLen,bool saveToDisk)
+int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
{
try {
- if (confLen <= 1)
- return 0;
-
- NetworkConfig newConfig;
-
- // Find the length of any string-serialized old-style Dictionary,
- // including its terminating NULL (if any). If this is before
- // the end of the config, that tells us there is a new-style
- // binary config which is preferred.
- unsigned int dictLen = 0;
- while (dictLen < confLen) {
- if (!(reinterpret_cast<const uint8_t *>(confBytes)[dictLen++]))
- break;
- }
-
- if (dictLen < (confLen - 2)) {
- Buffer<8194> tmp(reinterpret_cast<const uint8_t *>(confBytes) + dictLen,confLen - dictLen);
- newConfig.deserialize(tmp,0);
- } else {
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- newConfig.fromDictionary(reinterpret_cast<const char *>(confBytes),confLen); // throws if invalid
-#else
- return 0;
-#endif
- }
-
- if (!newConfig)
- return 0;
-
{
Mutex::Lock _l(_lock);
- if (_config == newConfig)
+ if (_config == nconf)
return 1; // OK config, but duplicate of what we already have
}
-
- if (applyConfiguration(newConfig)) {
+ if (applyConfiguration(nconf)) {
if (saveToDisk) {
- char n[128];
+ char n[64];
Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- RR->node->dataStorePut(n,confBytes,confLen,true);
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> d;
+ if (nconf.toDictionary(d,false))
+ RR->node->dataStorePut(n,(const void *)d.data(),d.sizeBytes(),true);
}
return 2; // OK and configuration has changed
}
@@ -235,12 +210,19 @@ void Network::requestConfiguration()
if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, uses locally generated static config
return;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> rmd;
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,(uint64_t)ZT_PROTO_VERSION);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MAJOR);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MINOR);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,(uint64_t)ZEROTIER_ONE_VERSION_REVISION);
+
if (controller() == RR->identity.address()) {
if (RR->localNetworkController) {
- Buffer<8194> tmp;
- switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,NetworkConfigRequestMetaData(),tmp)) {
+ NetworkConfig nconf;
+ switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,nconf)) {
case NetworkController::NETCONF_QUERY_OK:
- this->setConfiguration(tmp.data(),tmp.size(),true);
+ this->setConfiguration(nconf,true);
return;
case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND:
this->setNotFound();
@@ -259,16 +241,13 @@ void Network::requestConfiguration()
TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,controller().toString().c_str());
- NetworkConfigRequestMetaData metaData;
- metaData.initWithDefaults();
- Buffer<4096> mds;
- metaData.serialize(mds); // this always includes legacy fields to support old controllers
-
Packet outp(controller(),RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append((uint64_t)_id);
- outp.append((uint16_t)mds.size());
- outp.append(mds.data(),mds.size());
+ const unsigned int rmdSize = rmd.sizeBytes();
+ outp.append((uint16_t)rmdSize);
+ outp.append((const void *)rmd.data(),rmdSize);
outp.append((_config) ? (uint64_t)_config.revision : (uint64_t)0);
+ outp.compress();
RR->sw->send(outp,true,0);
}
@@ -337,21 +316,9 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
_announceMulticastGroups();
}
-void Network::setEnabled(bool enabled)
-{
- Mutex::Lock _l(_lock);
- if (_enabled != enabled) {
- _enabled = enabled;
- ZT_VirtualNetworkConfig ctmp;
- _externalConfig(&ctmp);
- _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE,&ctmp);
- }
-}
-
void Network::destroy()
{
Mutex::Lock _l(_lock);
- _enabled = false;
_destroyed = true;
}
@@ -388,15 +355,8 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->bridge = ((_config.allowPassiveBridging())||(std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end())) ? 1 : 0;
ec->broadcastEnabled = (_config) ? (_config.enableBroadcast() ? 1 : 0) : 0;
ec->portError = _portError;
- ec->enabled = (_enabled) ? 1 : 0;
ec->netconfRevision = (_config) ? (unsigned long)_config.revision : 0;
- ec->multicastSubscriptionCount = std::min((unsigned int)_myMulticastGroups.size(),(unsigned int)ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS);
- for(unsigned int i=0;i<ec->multicastSubscriptionCount;++i) {
- ec->multicastSubscriptions[i].mac = _myMulticastGroups[i].mac().toInt();
- ec->multicastSubscriptions[i].adi = _myMulticastGroups[i].adi();
- }
-
ec->assignedAddressCount = 0;
for(unsigned int i=0;i<ZT_MAX_ZT_ASSIGNED_ADDRESSES;++i) {
if (i < _config.staticIpCount) {
@@ -406,6 +366,16 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
memset(&(ec->assignedAddresses[i]),0,sizeof(struct sockaddr_storage));
}
}
+
+ ec->routeCount = 0;
+ for(unsigned int i=0;i<ZT_MAX_NETWORK_ROUTES;++i) {
+ if (i < _config.routeCount) {
+ memcpy(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
+ ++ec->routeCount;
+ } else {
+ memset(&(ec->routes[i]),0,sizeof(ZT_VirtualNetworkRoute));
+ }
+ }
}
bool Network::_isAllowed(const SharedPtr<Peer> &peer) const
diff --git a/zerotierone/node/Network.hpp b/zerotierone/node/Network.hpp
index 9d280fb..17eed4b 100644
--- a/zerotierone/node/Network.hpp
+++ b/zerotierone/node/Network.hpp
@@ -151,12 +151,11 @@ public:
/**
* Set or update this network's configuration
*
- * @param confBytes Network configuration in old-style Dictionary or new-style serialized format
- * @param confLen Length of network configuration in bytes
+ * @param nconf Network configuration
* @param saveToDisk IF true (default), write config to disk
* @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
*/
- int setConfiguration(const void *confBytes,unsigned int confLen,bool saveToDisk);
+ int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
/**
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
@@ -281,16 +280,6 @@ public:
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
/**
- * @return True if traffic on this network's tap is enabled
- */
- inline bool enabled() const throw() { return _enabled; }
-
- /**
- * @param enabled Should traffic be allowed on this network?
- */
- void setEnabled(bool enabled);
-
- /**
* Destroy this network
*
* This causes the network to disable itself, destroy its tap device, and on
@@ -323,7 +312,6 @@ private:
void *_uPtr;
uint64_t _id;
MAC _mac; // local MAC address
- volatile bool _enabled;
volatile bool _portInitialized;
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
diff --git a/zerotierone/node/NetworkConfig.cpp b/zerotierone/node/NetworkConfig.cpp
index 58a48fa..9d5c5f1 100644
--- a/zerotierone/node/NetworkConfig.cpp
+++ b/zerotierone/node/NetworkConfig.cpp
@@ -23,158 +23,479 @@
namespace ZeroTier {
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
-
-void NetworkConfig::fromDictionary(const char *ds,unsigned int dslen)
+bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
{
- static const std::string zero("0");
- static const std::string one("1");
-
- Dictionary d(ds,dslen);
+ Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
- memset(this,0,sizeof(NetworkConfig));
+ d.clear();
- // NOTE: d.get(name) throws if not found, d.get(name,default) returns default
+ // Try to put the more human-readable fields first
- networkId = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,"0").c_str());
- if (!networkId)
- throw std::invalid_argument("configuration contains zero network ID");
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
- timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,"0").c_str());
- revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older controllers don't send this, so default to 1
- issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,"0"));
+#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
+ if (includeLegacy) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
- multicastLimit = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,zero).c_str());
- if (multicastLimit == 0) multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
+ std::string v4s;
+ for(unsigned int i=0;i<staticIpCount;++i) {
+ if (this->staticIps[i].ss_family == AF_INET) {
+ if (v4s.length() > 0)
+ v4s.push_back(',');
+ v4s.append(this->staticIps[i].toString());
+ }
+ }
+ if (v4s.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
+ }
+ std::string v6s;
+ for(unsigned int i=0;i<staticIpCount;++i) {
+ if (this->staticIps[i].ss_family == AF_INET6) {
+ if (v6s.length() > 0)
+ v6s.push_back(',');
+ v6s.append(this->staticIps[i].toString());
+ }
+ }
+ if (v6s.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
+ }
- flags |= ((Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING,zero).c_str()) != 0) ? ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING : 0);
- flags |= ((Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST,one).c_str()) != 0) ? ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST : 0);
+ std::string ets;
+ unsigned int et = 0;
+ ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ for(unsigned int i=0;i<ruleCount;++i) {
+ ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
+ if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
+ et = rules[i].v.etherType;
+ } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
+ if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
+ if (ets.length() > 0)
+ ets.push_back(',');
+ char tmp[16];
+ Utils::snprintf(tmp,sizeof(tmp),"%x",et);
+ ets.append(tmp);
+ }
+ et = 0;
+ }
+ lastrt = rt;
+ }
+ if (ets.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
+ }
- this->type = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE,one).c_str()) != 0) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
+ if (this->com) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
+ }
- std::string nametmp(d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,""));
- for(unsigned long i=0;((i<ZT_MAX_NETWORK_SHORT_NAME_LENGTH)&&(i<nametmp.length()));++i)
- name[i] = (char)nametmp[i];
- // we zeroed the entire structure above and _name is ZT_MAX_NETWORK_SHORT_NAME_LENGTH+1, so it will always null-terminate
+ std::string ab;
+ for(unsigned int i=0;i<this->specialistCount;++i) {
+ if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
+ if (ab.length() > 0)
+ ab.push_back(',');
+ ab.append(Address(this->specialists[i]).toString().c_str());
+ }
+ }
+ if (ab.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
+ }
- std::vector<std::string> activeBridgesSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES,"").c_str(),",","",""));
- for(std::vector<std::string>::const_iterator a(activeBridgesSplit.begin());a!=activeBridgesSplit.end();++a) {
- if (a->length() == ZT_ADDRESS_LENGTH_HEX) { // ignore empty or garbage fields
- Address tmp(*a);
- if (!tmp.isReserved()) {
- uint64_t specialist = tmp.toInt();
- for(unsigned int i=0;i<specialistCount;++i) {
- if ((specialists[i] & 0xffffffffffULL) == specialist) {
- specialists[i] |= ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE;
- specialist = 0;
- break;
- }
- }
- if ((specialist)&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS))
- specialists[specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE;
+ std::vector<Relay> rvec(this->relays());
+ std::string rl;
+ for(std::vector<Relay>::const_iterator i(rvec.begin());i!=rvec.end();++i) {
+ if (rl.length() > 0)
+ rl.push_back(',');
+ rl.append(i->address.toString());
+ if (i->phy4) {
+ rl.push_back(';');
+ rl.append(i->phy4.toString());
+ } else if (i->phy6) {
+ rl.push_back(';');
+ rl.append(i->phy6.toString());
}
}
+ if (rl.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,rl.c_str())) return false;
+ }
}
+#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
- std::string ipAddrs(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC,std::string()));
- {
- std::string v6s(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC,std::string()));
- if (v6s.length()) {
- if (ipAddrs.length())
- ipAddrs.push_back(',');
- ipAddrs.append(v6s);
- }
+ // Then add binary blobs
+
+ if (this->com) {
+ tmp.clear();
+ this->com.serialize(tmp);
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) return false;
}
- std::vector<std::string> ipAddrsSplit(Utils::split(ipAddrs.c_str(),",","",""));
- for(std::vector<std::string>::const_iterator ipstr(ipAddrsSplit.begin());ipstr!=ipAddrsSplit.end();++ipstr) {
- InetAddress addr(*ipstr);
- switch(addr.ss_family) {
- case AF_INET:
- if ((!addr.netmaskBits())||(addr.netmaskBits() > 32))
- continue;
- break;
- case AF_INET6:
- if ((!addr.netmaskBits())||(addr.netmaskBits() > 128))
- continue;
- break;
- default: // ignore unrecognized address types or junk/empty fields
- continue;
- }
- if (!addr.isNetwork()) {
- if ((staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)&&(std::find(&(staticIps[0]),&(staticIps[staticIpCount]),addr) == &(staticIps[staticIpCount])))
- staticIps[staticIpCount++] = addr;
+
+ tmp.clear();
+ for(unsigned int i=0;i<this->specialistCount;++i) {
+ tmp.append((uint64_t)this->specialists[i]);
+ }
+ if (tmp.size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) return false;
+ }
+
+ tmp.clear();
+ for(unsigned int i=0;i<this->routeCount;++i) {
+ reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(tmp);
+ reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(tmp);
+ tmp.append((uint16_t)this->routes[i].flags);
+ tmp.append((uint16_t)this->routes[i].metric);
+ }
+ if (tmp.size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) return false;
+ }
+
+ tmp.clear();
+ for(unsigned int i=0;i<this->staticIpCount;++i) {
+ this->staticIps[i].serialize(tmp);
+ }
+ if (tmp.size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) return false;
+ }
+
+ tmp.clear();
+ for(unsigned int i=0;i<this->pinnedCount;++i) {
+ this->pinned[i].zt.appendTo(tmp);
+ this->pinned[i].phy.serialize(tmp);
+ }
+ if (tmp.size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) return false;
+ }
+
+ tmp.clear();
+ for(unsigned int i=0;i<this->ruleCount;++i) {
+ tmp.append((uint8_t)rules[i].t);
+ switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
+ //case ZT_NETWORK_RULE_ACTION_DROP:
+ //case ZT_NETWORK_RULE_ACTION_ACCEPT:
+ default:
+ tmp.append((uint8_t)0);
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ tmp.append((uint8_t)5);
+ Address(rules[i].v.zt).appendTo(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ tmp.append((uint8_t)2);
+ tmp.append((uint16_t)rules[i].v.vlanId);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ tmp.append((uint8_t)1);
+ tmp.append((uint8_t)rules[i].v.vlanPcp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ tmp.append((uint8_t)1);
+ tmp.append((uint8_t)rules[i].v.vlanDei);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ tmp.append((uint8_t)2);
+ tmp.append((uint16_t)rules[i].v.etherType);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ tmp.append((uint8_t)6);
+ tmp.append(rules[i].v.mac,6);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ tmp.append((uint8_t)5);
+ tmp.append(&(rules[i].v.ipv4.ip),4);
+ tmp.append((uint8_t)rules[i].v.ipv4.mask);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ tmp.append((uint8_t)17);
+ tmp.append(rules[i].v.ipv6.ip,16);
+ tmp.append((uint8_t)rules[i].v.ipv6.mask);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ tmp.append((uint8_t)1);
+ tmp.append((uint8_t)rules[i].v.ipTos);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ tmp.append((uint8_t)1);
+ tmp.append((uint8_t)rules[i].v.ipProtocol);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ tmp.append((uint8_t)4);
+ tmp.append((uint16_t)rules[i].v.port[0]);
+ tmp.append((uint16_t)rules[i].v.port[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ tmp.append((uint8_t)8);
+ tmp.append((uint64_t)rules[i].v.characteristics);
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ tmp.append((uint8_t)4);
+ tmp.append((uint16_t)rules[i].v.frameSize[0]);
+ tmp.append((uint16_t)rules[i].v.frameSize[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
+ tmp.append((uint8_t)8);
+ tmp.append((uint32_t)rules[i].v.tcpseq[0]);
+ tmp.append((uint32_t)rules[i].v.tcpseq[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
+ case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
+ tmp.append((uint8_t)16);
+ tmp.append((uint64_t)rules[i].v.comIV[0]);
+ tmp.append((uint64_t)rules[i].v.comIV[1]);
+ break;
}
}
- std::sort(&(staticIps[0]),&(staticIps[staticIpCount]));
-
- /* Old versions don't support gateways anyway, so ignore this in old netconfs
- std::vector<std::string> gatewaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS,"").c_str(),",","",""));
- for(std::vector<std::string>::const_iterator gwstr(gatewaysSplit.begin());gwstr!=gatewaysSplit.end();++gwstr) {
- InetAddress gw(*gwstr);
- if ((gw)&&(_gatewayCount < ZT_MAX_NETWORK_GATEWAYS)&&(std::find(&(_gateways[0]),&(_gateways[_gatewayCount]),gw) == &(_gateways[_gatewayCount])))
- _gateways[_gatewayCount++] = gw;
+ if (tmp.size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) return false;
}
- std::sort(&(_gateways[0]),&(_gateways[_gatewayCount]));
- */
-
- std::vector<std::string> relaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS,"").c_str(),",","",""));
- for(std::vector<std::string>::const_iterator r(relaysSplit.begin());r!=relaysSplit.end();++r) {
- if (r->length() >= ZT_ADDRESS_LENGTH_HEX) {
- Address zt(r->substr(0,ZT_ADDRESS_LENGTH_HEX).c_str());
- InetAddress phy[2];
- unsigned int phyCount = 0;
- const std::size_t semi(r->find(';'));
- if ((semi > ZT_ADDRESS_LENGTH_HEX)&&(semi < (r->length() - 2))) {
- std::vector<std::string> phySplit(Utils::split(r->substr(semi+1).c_str(),",","",""));
- for(std::vector<std::string>::const_iterator p(phySplit.begin());((p!=phySplit.end())&&(phyCount < 2));++p) {
- phy[phyCount] = InetAddress(*p);
- if (phy[phyCount])
- ++phyCount;
- else phy[phyCount].zero();
+
+ return true;
+}
+
+bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
+{
+ try {
+ Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
+ char tmp2[ZT_NETWORKCONFIG_DICT_CAPACITY];
+
+ memset(this,0,sizeof(NetworkConfig));
+
+ // Fields that are always present, new or old
+ this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
+ if (!this->networkId)
+ return false;
+ this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
+ this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
+ this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
+ if (!this->issuedTo)
+ return false;
+ this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
+ d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
+
+ if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
+ #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
+ // Decode legacy fields if version is old
+ if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
+ this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
+ if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD))
+ this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
+ this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
+ this->type = (d.getB(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,true)) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
+ if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
+ InetAddress ip(f);
+ if (!ip.isNetwork())
+ this->staticIps[this->staticIpCount++] = ip;
+ }
+ }
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
+ if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
+ InetAddress ip(f);
+ if (!ip.isNetwork())
+ this->staticIps[this->staticIpCount++] = ip;
+ }
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,tmp2,sizeof(tmp2)) > 0) {
+ this->com.fromString(tmp2);
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,tmp2,sizeof(tmp2)) > 0) {
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
+ unsigned int et = Utils::hexStrToUInt(f) & 0xffff;
+ if ((this->ruleCount + 2) > ZT_MAX_NETWORK_RULES) break;
+ if (et > 0) {
+ this->rules[this->ruleCount].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE;
+ this->rules[this->ruleCount].v.etherType = (uint16_t)et;
+ ++this->ruleCount;
+ }
+ this->rules[this->ruleCount++].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+ }
+ } else {
+ this->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ this->ruleCount = 1;
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
+ this->addSpecialist(Address(f),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
}
}
- uint64_t specialist = zt.toInt();
- for(unsigned int i=0;i<specialistCount;++i) {
- if ((specialists[i] & 0xffffffffffULL) == specialist) {
- specialists[i] |= ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY;
- specialist = 0;
- break;
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,tmp2,sizeof(tmp2)) > 0) {
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
+ char tmp3[256];
+ Utils::scopy(tmp3,sizeof(tmp3),f);
+
+ InetAddress phy;
+ char *semi = tmp3;
+ while (*semi) {
+ if (*semi == ';') {
+ *semi = (char)0;
+ ++semi;
+ phy = InetAddress(semi);
+ } else ++semi;
+ }
+ Address zt(tmp3);
+
+ this->addSpecialist(zt,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
+ if ((phy)&&(this->pinnedCount < ZT_MAX_NETWORK_PINNED)) {
+ this->pinned[this->pinnedCount].zt = zt;
+ this->pinned[this->pinnedCount].phy = phy;
+ ++this->pinnedCount;
+ }
}
}
+ #else
+ return false;
+ #endif // ZT_SUPPORT_OLD_STYLE_NETCONF
+ } else {
+ // Otherwise we can use the new fields
+ this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
+ this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
- if ((specialist)&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS))
- specialists[specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY;
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) {
+ this->com.deserialize(tmp,0);
+ }
- if ((phy[0])&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
- pinned[pinnedCount].zt = zt;
- pinned[pinnedCount].phy = phy[0];
- ++pinnedCount;
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) {
+ unsigned int p = 0;
+ while (((p + 8) <= tmp.size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
+ this->specialists[this->specialistCount++] = tmp.at<uint64_t>(p);
+ p += 8;
+ }
}
- if ((phy[1])&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
- pinned[pinnedCount].zt = zt;
- pinned[pinnedCount].phy = phy[0];
- ++pinnedCount;
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) {
+ unsigned int p = 0;
+ while ((p < tmp.size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
+ p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(tmp,p);
+ p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(tmp,p);
+ this->routes[this->routeCount].flags = tmp.at<uint16_t>(p); p += 2;
+ this->routes[this->routeCount].metric = tmp.at<uint16_t>(p); p += 2;
+ ++this->routeCount;
+ }
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) {
+ unsigned int p = 0;
+ while ((p < tmp.size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ p += this->staticIps[this->staticIpCount++].deserialize(tmp,p);
+ }
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) {
+ unsigned int p = 0;
+ while ((p < tmp.size())&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
+ this->pinned[this->pinnedCount].zt.setTo(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ p += this->pinned[this->pinnedCount].phy.deserialize(tmp,p);
+ ++this->pinnedCount;
+ }
}
- }
- }
- std::vector<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","",""));
- for(std::vector<std::string>::const_iterator et(ets.begin());et!=ets.end();++et) {
- unsigned int et2 = Utils::hexStrToUInt(et->c_str()) & 0xffff;
- if ((ruleCount + 1) < ZT_MAX_NETWORK_RULES) {
- if (et2) {
- rules[ruleCount].t = ZT_NETWORK_RULE_MATCH_ETHERTYPE;
- rules[ruleCount].v.etherType = (uint16_t)et2;
- ++ruleCount;
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) {
+ unsigned int p = 0;
+ while ((p < tmp.size())&&(ruleCount < ZT_MAX_NETWORK_RULES)) {
+ rules[ruleCount].t = (uint8_t)tmp[p++];
+ unsigned int fieldLen = (unsigned int)tmp[p++];
+ switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x7f)) {
+ default:
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ rules[ruleCount].v.zt = Address(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ rules[ruleCount].v.vlanId = tmp.at<uint16_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ rules[ruleCount].v.vlanPcp = (uint8_t)tmp[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ rules[ruleCount].v.vlanDei = (uint8_t)tmp[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ rules[ruleCount].v.etherType = tmp.at<uint16_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ memcpy(rules[ruleCount].v.mac,tmp.field(p,6),6);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ memcpy(&(rules[ruleCount].v.ipv4.ip),tmp.field(p,4),4);
+ rules[ruleCount].v.ipv4.mask = (uint8_t)tmp[p + 4];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ memcpy(rules[ruleCount].v.ipv6.ip,tmp.field(p,16),16);
+ rules[ruleCount].v.ipv6.mask = (uint8_t)tmp[p + 16];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ rules[ruleCount].v.ipTos = (uint8_t)tmp[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ rules[ruleCount].v.ipProtocol = (uint8_t)tmp[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ rules[ruleCount].v.port[0] = tmp.at<uint16_t>(p);
+ rules[ruleCount].v.port[1] = tmp.at<uint16_t>(p + 2);
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ rules[ruleCount].v.characteristics = tmp.at<uint64_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p);
+ rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p + 2);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
+ rules[ruleCount].v.tcpseq[0] = tmp.at<uint32_t>(p);
+ rules[ruleCount].v.tcpseq[1] = tmp.at<uint32_t>(p + 4);
+ break;
+ case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
+ case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
+ rules[ruleCount].v.comIV[0] = tmp.at<uint64_t>(p);
+ rules[ruleCount].v.comIV[1] = tmp.at<uint64_t>(p + 8);
+ break;
+ }
+ p += fieldLen;
+ ++ruleCount;
+ }
}
- rules[ruleCount++].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
}
- }
- this->com.fromString(d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP,std::string()));
-}
+ //printf("~~~\n%s\n~~~\n",d.data());
+ //dump();
+ //printf("~~~\n");
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
+ return true;
+ } catch ( ... ) {
+ return false;
+ }
+}
} // namespace ZeroTier
diff --git a/zerotierone/node/NetworkConfig.hpp b/zerotierone/node/NetworkConfig.hpp
index a7ed77b..5271c5a 100644
--- a/zerotierone/node/NetworkConfig.hpp
+++ b/zerotierone/node/NetworkConfig.hpp
@@ -35,21 +35,22 @@
#include "MulticastGroup.hpp"
#include "Address.hpp"
#include "CertificateOfMembership.hpp"
-
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
#include "Dictionary.hpp"
-#include <string>
-#endif
/**
* Flag: allow passive bridging (experimental)
*/
-#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0001
+#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL
/**
* Flag: enable broadcast
*/
-#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0002
+#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0000000000000002ULL
+
+/**
+ * Flag: enable IPv6 NDP emulation for certain V6 address patterns
+ */
+#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
/**
* Device is a network preferred relay
@@ -68,18 +69,23 @@
namespace ZeroTier {
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
+// Maximum size of a network config dictionary (can be increased)
+#define ZT_NETWORKCONFIG_DICT_CAPACITY 8194
+
+// Network config version
+#define ZT_NETWORKCONFIG_VERSION 6
// Fields for meta-data sent with network config requests
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
-// These dictionary keys are short so they don't take up much room in
-// netconf response packets.
+// These dictionary keys are short so they don't take up much room.
-// integer(hex)[,integer(hex),...]
-#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et"
+// network config version
+#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
// network ID
#define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
// integer(hex)
@@ -88,34 +94,49 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
// address of member
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
+// flags(hex)
+#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
// integer(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
-// 0/1
-#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p"
+// network type (hex)
+#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
// text
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
-// text
-#define ZT_NETWORKCONFIG_DICT_KEY_DESC "d"
+// binary serialized certificate of membership
+#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
+// specialists (binary array of uint64_t)
+#define ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS "S"
+// routes (binary blob)
+#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
+// static IPs (binary blob)
+#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
+// pinned address physical route mappings (binary blob)
+#define ZT_NETWORKCONFIG_DICT_KEY_PINNED "P"
+// rules (binary blob)
+#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
+
+// Legacy fields -- these are obsoleted but are included when older clients query
+
+// boolean (now a flag)
+#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
+// boolean (now a flag)
+#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
// IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them.
-#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC "v4s"
+#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD "v4s"
// IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them.
-#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC "v6s"
-// serialized CertificateOfMembership
-#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP "com"
+#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD "v6s"
// 0/1
-#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST "eb"
-// 0/1
-#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING "pb"
+#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD "p"
+// integer(hex)[,integer(hex),...]
+#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD "et"
+// string-serialized CertificateOfMembership
+#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD "com"
// node[,node,...]
-#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES "ab"
+#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD "ab"
// node;IP/port[,node;IP/port]
-#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS "rl"
-// IP/metric[,IP/metric,...]
-#define ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS "gw"
-
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
+#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
/**
* Network configuration received from network controller nodes
@@ -215,6 +236,23 @@ public:
}
/**
+ * Write this network config to a dictionary for transport
+ *
+ * @param d Dictionary
+ * @param includeLegacy If true, include legacy fields for old node versions
+ * @return True if dictionary was successfully created, false if e.g. overflow
+ */
+ bool toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const;
+
+ /**
+ * Read this network config from a dictionary
+ *
+ * @param d Dictionary
+ * @return True if dictionary was valid and network config successfully initialized
+ */
+ bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
+
+ /**
* @return True if passive bridging is allowed (experimental)
*/
inline bool allowPassiveBridging() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0); }
@@ -225,6 +263,11 @@ public:
inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
/**
+ * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
+ */
+ inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
+
+ /**
* @return Network type is public (no access control)
*/
inline bool isPublic() const throw() { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
@@ -322,9 +365,10 @@ public:
{
while (ptr < specialistCount) {
if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
- return Address(specialists[ptr]);
+ return Address(specialists[ptr++]);
+ } else {
+ ++ptr;
}
- ++ptr;
}
return Address();
}
@@ -350,265 +394,6 @@ public:
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
- template<unsigned int C>
- inline void serialize(Buffer<C> &b) const
- {
- b.append((uint16_t)1); // version
-
- b.append((uint64_t)networkId);
- b.append((uint64_t)timestamp);
- b.append((uint64_t)revision);
- issuedTo.appendTo(b);
- b.append((uint32_t)multicastLimit);
- b.append((uint32_t)flags);
- b.append((uint8_t)type);
-
- unsigned int nl = (unsigned int)strlen(name);
- if (nl > 255) nl = 255; // sanity check
- b.append((uint8_t)nl);
- b.append((const void *)name,nl);
-
- b.append((uint16_t)specialistCount);
- for(unsigned int i=0;i<specialistCount;++i)
- b.append((uint64_t)specialists[i]);
-
- b.append((uint16_t)routeCount);
- for(unsigned int i=0;i<routeCount;++i) {
- reinterpret_cast<const InetAddress *>(&(routes[i].target))->serialize(b);
- reinterpret_cast<const InetAddress *>(&(routes[i].via))->serialize(b);
- }
-
- b.append((uint16_t)staticIpCount);
- for(unsigned int i=0;i<staticIpCount;++i)
- staticIps[i].serialize(b);
-
- b.append((uint16_t)pinnedCount);
- for(unsigned int i=0;i<pinnedCount;++i) {
- pinned[i].zt.appendTo(b);
- pinned[i].phy.serialize(b);
- }
-
- b.append((uint16_t)ruleCount);
- for(unsigned int i=0;i<ruleCount;++i) {
- b.append((uint8_t)rules[i].t);
- switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
- //case ZT_NETWORK_RULE_ACTION_DROP:
- //case ZT_NETWORK_RULE_ACTION_ACCEPT:
- default:
- b.append((uint8_t)0);
- break;
- case ZT_NETWORK_RULE_ACTION_TEE:
- case ZT_NETWORK_RULE_ACTION_REDIRECT:
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
- b.append((uint8_t)5);
- Address(rules[i].v.zt).appendTo(b);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_ID:
- b.append((uint8_t)2);
- b.append((uint16_t)rules[i].v.vlanId);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
- b.append((uint8_t)1);
- b.append((uint8_t)rules[i].v.vlanPcp);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
- b.append((uint8_t)1);
- b.append((uint8_t)rules[i].v.vlanDei);
- break;
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
- b.append((uint8_t)2);
- b.append((uint16_t)rules[i].v.etherType);
- break;
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
- case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- b.append((uint8_t)6);
- b.append(rules[i].v.mac,6);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- b.append((uint8_t)5);
- b.append(&(rules[i].v.ipv4.ip),4);
- b.append((uint8_t)rules[i].v.ipv4.mask);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- b.append((uint8_t)17);
- b.append(rules[i].v.ipv6.ip,16);
- b.append((uint8_t)rules[i].v.ipv6.mask);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_TOS:
- b.append((uint8_t)1);
- b.append((uint8_t)rules[i].v.ipTos);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
- b.append((uint8_t)1);
- b.append((uint8_t)rules[i].v.ipProtocol);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
- b.append((uint8_t)4);
- b.append((uint16_t)rules[i].v.port[0]);
- b.append((uint16_t)rules[i].v.port[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
- b.append((uint8_t)8);
- b.append((uint64_t)rules[i].v.characteristics);
- break;
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
- b.append((uint8_t)4);
- b.append((uint16_t)rules[i].v.frameSize[0]);
- b.append((uint16_t)rules[i].v.frameSize[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- b.append((uint8_t)8);
- b.append((uint32_t)rules[i].v.tcpseq[0]);
- b.append((uint32_t)rules[i].v.tcpseq[1]);
- break;
- }
- }
-
- this->com.serialize(b);
-
- b.append((uint16_t)0); // extended bytes, currently 0 since unused
- }
-
- template<unsigned int C>
- inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
- {
- memset(this,0,sizeof(NetworkConfig));
-
- unsigned int p = startAt;
-
- if (b.template at<uint16_t>(p) != 1)
- throw std::invalid_argument("unrecognized version");
- p += 2;
-
- networkId = b.template at<uint64_t>(p); p += 8;
- timestamp = b.template at<uint64_t>(p); p += 8;
- revision = b.template at<uint64_t>(p); p += 8;
- issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
- multicastLimit = (unsigned int)b.template at<uint32_t>(p); p += 4;
- flags = (unsigned int)b.template at<uint32_t>(p); p += 4;
- type = (ZT_VirtualNetworkType)b[p++];
-
- unsigned int nl = (unsigned int)b[p++];
- memcpy(this->name,b.field(p,nl),std::min(nl,(unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH));
- p += nl;
- // _name will always be null terminated since field size is ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1
-
- specialistCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
- if (specialistCount > ZT_MAX_NETWORK_SPECIALISTS)
- throw std::invalid_argument("overflow (specialists)");
- for(unsigned int i=0;i<specialistCount;++i) {
- specialists[i] = b.template at<uint64_t>(p); p += 8;
- }
-
- routeCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
- if (routeCount > ZT_MAX_NETWORK_ROUTES)
- throw std::invalid_argument("overflow (routes)");
- for(unsigned int i=0;i<routeCount;++i) {
- p += reinterpret_cast<InetAddress *>(&(routes[i].target))->deserialize(b,p);
- p += reinterpret_cast<InetAddress *>(&(routes[i].via))->deserialize(b,p);
- }
-
- staticIpCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
- if (staticIpCount > ZT_MAX_ZT_ASSIGNED_ADDRESSES)
- throw std::invalid_argument("overflow (static IPs)");
- for(unsigned int i=0;i<staticIpCount;++i) {
- p += staticIps[i].deserialize(b,p);
- }
-
- pinnedCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
- if (pinnedCount > ZT_MAX_NETWORK_PINNED)
- throw std::invalid_argument("overflow (static addresses)");
- for(unsigned int i=0;i<pinnedCount;++i) {
- pinned[i].zt.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
- p += pinned[i].phy.deserialize(b,p);
- }
-
- ruleCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
- if (ruleCount > ZT_MAX_NETWORK_RULES)
- throw std::invalid_argument("overflow (rules)");
- for(unsigned int i=0;i<ruleCount;++i) {
- rules[i].t = (uint8_t)b[p++];
- unsigned int rlen = (unsigned int)b[p++];
- switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
- //case ZT_NETWORK_RULE_ACTION_DROP:
- //case ZT_NETWORK_RULE_ACTION_ACCEPT:
- default:
- break;
- case ZT_NETWORK_RULE_ACTION_TEE:
- case ZT_NETWORK_RULE_ACTION_REDIRECT:
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: {
- Address tmp;
- tmp.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- rules[i].v.zt = tmp.toInt();
- } break;
- case ZT_NETWORK_RULE_MATCH_VLAN_ID:
- rules[i].v.vlanId = b.template at<uint16_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
- rules[i].v.vlanPcp = (uint8_t)b[p];
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
- rules[i].v.vlanDei = (uint8_t)b[p];
- break;
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
- rules[i].v.etherType = b.template at<uint16_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
- case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- memcpy(rules[i].v.mac,b.field(p,6),6);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- memcpy(&(rules[i].v.ipv4.ip),b.field(p,4),4);
- rules[i].v.ipv4.mask = (uint8_t)b[p+4];
- break;
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- memcpy(rules[i].v.ipv6.ip,b.field(p,16),16);
- rules[i].v.ipv6.mask = (uint8_t)b[p+16];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_TOS:
- rules[i].v.ipTos = (uint8_t)b[p];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
- rules[i].v.ipProtocol = (uint8_t)b[p];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
- rules[i].v.port[0] = b.template at<uint16_t>(p);
- rules[i].v.port[1] = b.template at<uint16_t>(p+2);
- break;
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
- rules[i].v.characteristics = b.template at<uint64_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
- rules[i].v.frameSize[0] = b.template at<uint16_t>(p);
- rules[i].v.frameSize[1] = b.template at<uint16_t>(p+2);
- break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- rules[i].v.tcpseq[0] = b.template at<uint32_t>(p);
- rules[i].v.tcpseq[1] = b.template at<uint32_t>(p + 4);
- break;
- }
- p += rlen;
- }
-
- p += this->com.deserialize(b,p);
-
- p += b.template at<uint16_t>(p) + 2;
-
- return (p - startAt);
- }
-
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- void fromDictionary(const char *ds,unsigned int dslen);
-#endif
-
/*
inline void dump() const
{
@@ -623,16 +408,18 @@ public:
printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
printf("routeCount==%u\n",routeCount);
for(unsigned int i=0;i<routeCount;++i) {
- printf(" routes[i].target==%s\n",reinterpret_cast<const struct sockaddr_storage *>(&(routes[i].target))->toString().c_str());
- printf(" routes[i].via==%s\n",reinterpret_cast<const struct sockaddr_storage *>(&(routes[i].via))->toString().c_str());
+ printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
+ printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
+ printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
+ printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
}
printf("staticIpCount==%u\n",staticIpCount);
for(unsigned int i=0;i<staticIpCount;++i)
printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
printf("pinnedCount==%u\n",pinnedCount);
for(unsigned int i=0;i<pinnedCount;++i) {
- printf(" pinned[i].zt==%s\n",pinned[i].zt->toString().c_str());
- printf(" pinned[i].phy==%s\n",pinned[i].zt->toString().c_str());
+ printf(" pinned[i].zt==%s\n",pinned[i].zt.toString().c_str());
+ printf(" pinned[i].phy==%s\n",pinned[i].phy.toString().c_str());
}
printf("ruleCount==%u\n",ruleCount);
printf("name==%s\n",name);
@@ -641,6 +428,32 @@ public:
*/
/**
+ * Add a specialist or mask flags if already present
+ *
+ * This masks the existing flags if the specialist is already here or adds
+ * it otherwise.
+ *
+ * @param a Address of specialist
+ * @param f Flags (OR of specialist role/type flags)
+ * @return True if successfully masked or added
+ */
+ inline bool addSpecialist(const Address &a,const uint64_t f)
+ {
+ const uint64_t aint = a.toInt();
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & 0xffffffffffULL) == aint) {
+ specialists[i] |= f;
+ return true;
+ }
+ }
+ if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) {
+ specialists[specialistCount++] = f | aint;
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Network ID that this configuration applies to
*/
uint64_t networkId;
@@ -661,14 +474,14 @@ public:
Address issuedTo;
/**
- * Maximum number of recipients per multicast (not including active bridges)
+ * Flags (64-bit)
*/
- unsigned int multicastLimit;
+ uint64_t flags;
/**
- * Flags (32-bit)
+ * Maximum number of recipients per multicast (not including active bridges)
*/
- unsigned int flags;
+ unsigned int multicastLimit;
/**
* Number of specialists
diff --git a/zerotierone/node/NetworkConfigRequestMetaData.hpp b/zerotierone/node/NetworkConfigRequestMetaData.hpp
deleted file mode 100644
index 2516e5e..0000000
--- a/zerotierone/node/NetworkConfigRequestMetaData.hpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_NETWORKCONFIGREQUESTMETADATA_HPP
-#define ZT_NETWORKCONFIGREQUESTMETADATA_HPP
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "Constants.hpp"
-#include "NetworkConfig.hpp"
-#include "Buffer.hpp"
-#include "Packet.hpp"
-
-#include "../version.h"
-
-/**
- * Maximum length of the auth field (including terminating NULL, since it's a C-style string)
- *
- * Actual max length not including NULL is this minus one.
- */
-#define ZT_NETWORK_CONFIG_REQUEST_METADATA_MAX_AUTH_LENGTH 2048
-
-namespace ZeroTier {
-
-/**
- * Network configuration request meta data
- */
-class NetworkConfigRequestMetaData
-{
-public:
- /**
- * Construct an empty meta-data object with zero/null values
- */
- NetworkConfigRequestMetaData()
- {
- memset(this,0,sizeof(NetworkConfigRequestMetaData));
- }
-
- /**
- * Initialize with defaults from this node's config and version
- */
- inline void initWithDefaults()
- {
- memset(this,0,sizeof(NetworkConfigRequestMetaData));
- vendor = ZT_VENDOR_ZEROTIER;
- platform = ZT_PLATFORM_UNSPECIFIED;
- architecture = ZT_ARCHITECTURE_UNSPECIFIED;
- majorVersion = ZEROTIER_ONE_VERSION_MAJOR;
- minorVersion = ZEROTIER_ONE_VERSION_MINOR;
- revision = ZEROTIER_ONE_VERSION_REVISION;
- protocolVersion = ZT_PROTO_VERSION;
- }
-
- /**
- * Zero/null everything
- */
- inline void clear()
- {
- memset(this,0,sizeof(NetworkConfigRequestMetaData));
- }
-
- template<unsigned int C>
- inline void serialize(Buffer<C> &b) const
- {
- /* Unlike network config we always send the old fields. Newer network
- * controllers will detect the presence of the new serialized data by
- * detecting extra data after the terminating NULL. But always sending
- * these maintains backward compatibility with old controllers. This
- * appends a terminating NULL which seperates the old legacy meta-data
- * from the new packed binary format that we send after. */
- b.appendCString("majv=" ZEROTIER_ONE_VERSION_MAJOR_S_HEX "\nminv=" ZEROTIER_ONE_VERSION_MINOR_S_HEX "\nrevv=" ZEROTIER_ONE_VERSION_REVISION_S_HEX "\n");
-
- b.append((uint16_t)1); // serialization version
-
- b.append((uint64_t)buildId);
- b.append((uint64_t)flags);
- b.append((uint16_t)vendor);
- b.append((uint16_t)platform);
- b.append((uint16_t)architecture);
- b.append((uint16_t)majorVersion);
- b.append((uint16_t)minorVersion);
- b.append((uint16_t)revision);
- b.append((uint16_t)protocolVersion);
-
- const unsigned int tl = strlen(auth);
- b.append((uint16_t)tl);
- b.append((const void *)auth,tl);
-
- b.append((uint16_t)0); // extended bytes, currently 0 since unused
- }
-
- template<unsigned int C>
- inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
- {
- memset(this,0,sizeof(NetworkConfigRequestMetaData));
-
- unsigned int p = startAt;
-
- // Seek past old style meta-data
- while (b[p]) ++p;
- ++p;
-
- if (b.template at<uint16_t>(p) != 1)
- throw std::invalid_argument("unrecognized version");
- p += 2;
-
- buildId = b.template at<uint64_t>(p); p += 8;
- flags = b.template at<uint64_t>(p); p += 8;
- vendor = (ZT_Vendor)b.template at<uint16_t>(p); p += 2;
- platform = (ZT_Platform)b.template at<uint16_t>(p); p += 2;
- architecture = (ZT_Architecture)b.template at<uint16_t>(p); p += 2;
- majorVersion = b.template at<uint16_t>(p); p += 2;
- minorVersion = b.template at<uint16_t>(p); p += 2;
- revision = b.template at<uint16_t>(p); p += 2;
- protocolVersion = b.template at<uint16_t>(p); p += 2;
-
- const unsigned int tl = b.template at<uint16_t>(p); p += 2;
- memcpy(auth,b.field(p,tl),std::max(tl,(unsigned int)(ZT_NETWORK_CONFIG_REQUEST_METADATA_MAX_AUTH_LENGTH - 1)));
- p += tl;
-
- p += b.template at<uint16_t>(p) + 2;
-
- return (p - startAt);
- }
-
- /**
- * Authentication data (e.g. bearer=<token>) as a C-style string (always null terminated)
- */
- char auth[ZT_NETWORK_CONFIG_REQUEST_METADATA_MAX_AUTH_LENGTH];
-
- /**
- * Build ID (currently unused, must be 0)
- */
- uint64_t buildId;
-
- /**
- * Flags (currently unused, must be 0)
- */
- uint64_t flags;
-
- /**
- * ZeroTier vendor or 0 for unspecified
- */
- ZT_Vendor vendor;
-
- /**
- * ZeroTier platform or 0 for unspecified
- */
- ZT_Platform platform;
-
- /**
- * ZeroTier architecture or 0 for unspecified
- */
- ZT_Architecture architecture;
-
- /**
- * ZeroTier software major version
- */
- unsigned int majorVersion;
-
- /**
- * ZeroTier software minor version
- */
- unsigned int minorVersion;
-
- /**
- * ZeroTier software revision
- */
- unsigned int revision;
-
- /**
- * ZeroTier protocol version
- */
- unsigned int protocolVersion;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/zerotierone/node/NetworkController.hpp b/zerotierone/node/NetworkController.hpp
index 4ab6403..fa90fb7 100644
--- a/zerotierone/node/NetworkController.hpp
+++ b/zerotierone/node/NetworkController.hpp
@@ -22,15 +22,15 @@
#include <stdint.h>
#include "Constants.hpp"
-#include "InetAddress.hpp"
-#include "Address.hpp"
-#include "Identity.hpp"
-#include "NetworkConfigRequestMetaData.hpp"
-#include "Buffer.hpp"
+#include "Dictionary.hpp"
+#include "NetworkConfig.hpp"
namespace ZeroTier {
class RuntimeEnvironment;
+class Identity;
+class Address;
+struct InetAddress;
/**
* Interface for network controller implementations
@@ -67,16 +67,16 @@ public:
* @param identity Originating peer ZeroTier identity
* @param nwid 64-bit network ID
* @param metaData Meta-data bundled with request (if any)
- * @param result Buffer to receive serialized network configuration data (any existing data in buffer is preserved)
- * @return Returns NETCONF_QUERY_OK if result dictionary is valid, or an error code on error
+ * @param nc NetworkConfig to fill with results
+ * @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
*/
virtual NetworkController::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr,
const Identity &signingId,
const Identity &identity,
uint64_t nwid,
- const NetworkConfigRequestMetaData &metaData,
- Buffer<8194> &result) = 0;
+ const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,
+ NetworkConfig &nc) = 0;
};
} // namespace ZeroTier
diff --git a/zerotierone/node/Node.cpp b/zerotierone/node/Node.cpp
index bedbba9..1308502 100644
--- a/zerotierone/node/Node.cpp
+++ b/zerotierone/node/Node.cpp
@@ -447,6 +447,7 @@ ZT_PeerList *Node::peers() const
p->paths[p->pathCount].lastReceive = path->lastReceived();
p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
+ p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->address());
++p->pathCount;
}
}
@@ -745,6 +746,11 @@ void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
(reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
}
+void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
+{
+ RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
+}
+
} // namespace ZeroTier
/****************************************************************************/
@@ -1014,6 +1020,13 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
} catch ( ... ) {}
}
+void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
+{
+ try {
+ reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
+ } catch ( ... ) {}
+}
+
void ZT_Node_backgroundThreadMain(ZT_Node *node)
{
try {
diff --git a/zerotierone/node/Node.hpp b/zerotierone/node/Node.hpp
index 6ac23ca..0a39d1e 100644
--- a/zerotierone/node/Node.hpp
+++ b/zerotierone/node/Node.hpp
@@ -248,26 +248,15 @@ public:
*/
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
- /**
- * @return True if we appear to be online
- */
inline bool online() const throw() { return _online; }
#ifdef ZT_TRACE
void postTrace(const char *module,unsigned int line,const char *fmt,...);
#endif
- /**
- * @return Next 64-bit random number (not for cryptographic use)
- */
uint64_t prng();
-
- /**
- * Post a circuit test report to any listeners for a given test ID
- *
- * @param report Report (includes test ID)
- */
void postCircuitTestReport(const ZT_CircuitTestReport *report);
+ void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
private:
inline SharedPtr<Network> _network(uint64_t nwid) const
diff --git a/zerotierone/node/Packet.hpp b/zerotierone/node/Packet.hpp
index b367ec6..3d95b0b 100644
--- a/zerotierone/node/Packet.hpp
+++ b/zerotierone/node/Packet.hpp
@@ -34,7 +34,11 @@
#include "Utils.hpp"
#include "Buffer.hpp"
+#ifdef ZT_USE_SYSTEM_LZ4
+#include <lz4.h>
+#else
#include "../ext/lz4/lz4.h"
+#endif
/**
* Protocol version -- incremented only for major changes
@@ -48,13 +52,18 @@
* + New crypto completely changes key agreement cipher
* 4 - 0.6.0 ... 1.0.6
* + New identity format based on hashcash design
- * 5 - 1.1.0 ... CURRENT
+ * 5 - 1.1.0 ... 1.1.5
* + Supports circuit test, proof of work, and echo
* + Supports in-band world (root server definition) updates
* + Clustering! (Though this will work with protocol v4 clients.)
* + Otherwise backward compatible with protocol v4
+ * 6 - 1.1.5 ... 1.1.10
+ * + Deprecate old dictionary-based network config format
+ * + Introduce new binary serialized network config and meta-data
+ * 7 - 1.1.10 -- CURRENT
+ * + Introduce trusted paths for local SDN use
*/
-#define ZT_PROTO_VERSION 5
+#define ZT_PROTO_VERSION 7
/**
* Minimum supported protocol version
@@ -93,10 +102,21 @@
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
/**
- * DEPRECATED payload encrypted flag, will be removed for re-use soon.
+ * Cipher suite: NONE
+ *
+ * This differs from POLY1305/NONE in that *no* crypto is done, not even
+ * authentication. This is for trusted local LAN interconnects for internal
+ * SDN use within a data center.
*
- * This has been replaced by the two-bit cipher suite selection field where
- * a value of 0 indicates unencrypted (but authenticated) messages.
+ * For this mode the MAC field becomes a trusted path ID and must match the
+ * configured ID of a trusted path or the packet is discarded.
+ */
+#define ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH 2
+
+/**
+ * DEPRECATED payload encrypted flag, may be re-used in the future.
+ *
+ * This has been replaced by the three-bit cipher suite selection field.
*/
#define ZT_PROTO_FLAG_ENCRYPTED 0x80
@@ -330,7 +350,7 @@ namespace ZeroTier {
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher/hops>
- * <[8] 64-bit MAC>
+ * <[8] 64-bit MAC (or trusted path ID in trusted path mode)>
* [... -- begin encryption envelope -- ...]
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
* [... verb-specific payload ...]
@@ -1211,7 +1231,6 @@ public:
*/
inline unsigned int cipher() const
{
- // Note: this uses the new cipher spec field, which is incompatible with <1.0.0 peers
return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3);
}
@@ -1222,13 +1241,31 @@ public:
{
unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS];
b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH
- // DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
+ // Set DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
b |= ZT_PROTO_FLAG_ENCRYPTED;
else b &= (~ZT_PROTO_FLAG_ENCRYPTED);
}
/**
+ * Get the trusted path ID for this packet (only meaningful if cipher is trusted path)
+ *
+ * @return Trusted path ID (from MAC field)
+ */
+ inline uint64_t trustedPathId() const { return at<uint64_t>(ZT_PACKET_IDX_MAC); }
+
+ /**
+ * Set this packet's trusted path ID and set the cipher spec to trusted path
+ *
+ * @param tpid Trusted path ID
+ */
+ inline void setTrusted(const uint64_t tpid)
+ {
+ setCipher(ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH);
+ setAt(ZT_PACKET_IDX_MAC,tpid);
+ }
+
+ /**
* Get this packet's unique ID (the IV field interpreted as uint64_t)
*
* @return Packet ID
@@ -1271,6 +1308,10 @@ public:
/**
* Verify and (if encrypted) decrypt packet
*
+ * This does not handle trusted path mode packets and will return false
+ * for these. These are handled in IncomingPacket if the sending physical
+ * address and MAC field match a trusted path.
+ *
* @param key 32-byte key
* @return False if packet is invalid or failed MAC authenticity check
*/
diff --git a/zerotierone/node/Path.hpp b/zerotierone/node/Path.hpp
index c88c295..ecf4be2 100644
--- a/zerotierone/node/Path.hpp
+++ b/zerotierone/node/Path.hpp
@@ -252,7 +252,7 @@ public:
*/
inline bool reliable() const throw()
{
- if (_addr.ss_family == AF_INET)
+ if ((_addr.ss_family == AF_INET)||(_addr.ss_family == AF_INET6))
return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE));
return true;
}
diff --git a/zerotierone/node/Switch.cpp b/zerotierone/node/Switch.cpp
index e7cda1b..bf3afe3 100644
--- a/zerotierone/node/Switch.cpp
+++ b/zerotierone/node/Switch.cpp
@@ -351,68 +351,87 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
return;
}
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
- /* IPv6 NDP emulation on ZeroTier-RFC4193 addressed networks! This allows
- * for multicast-free operation in IPv6 networks, which both improves
- * performance and is friendlier to mobile and (especially) IoT devices.
- * In the future there may be a no-multicast build option for embedded
- * and IoT use and this will be the preferred addressing mode. Note that
- * it plays nice with our L2 emulation philosophy and even with bridging.
- * While "real" devices behind the bridge can't have ZT-RFC4193 addresses
- * themselves, they can look these addresses up with NDP and it will
- * work just fine. */
- if ((reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
+ // IPv6 NDP emulation for certain very special patterns of private IPv6 addresses -- if enabled
+ if ((network->config().ndpEmulation())&&(reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
+ Address v6EmbeddedAddress;
+ const uint8_t *const pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
+ const uint8_t *my6 = (const uint8_t *)0;
+
+ // ZT-RFC4193 address: fdNN:NNNN:NNNN:NNNN:NN99:93DD:DDDD:DDDD / 88 (one /128 per actual host)
+
+ // ZT-6PLANE address: fcXX:XXXX:XXDD:DDDD:DDDD:####:####:#### / 40 (one /80 per actual host)
+ // (XX - lower 32 bits of network ID XORed with higher 32 bits)
+
+ // For these to work, we must have a ZT-managed address assigned in one of the
+ // above formats, and the query must match its prefix.
for(unsigned int sipk=0;sipk<network->config().staticIpCount;++sipk) {
- const InetAddress *sip = &(network->config().staticIps[sipk]);
- if ((sip->ss_family == AF_INET6)&&(Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port) == 88)) {
- const uint8_t *my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
- if ((my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 == fd__:____:____:____:__99:93__:____:____ / 88
- const uint8_t *pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
+ const InetAddress *const sip = &(network->config().staticIps[sipk]);
+ if (sip->ss_family == AF_INET6) {
+ my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
+ const unsigned int sipNetmaskBits = Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port);
+ if ((sipNetmaskBits == 88)&&(my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 /88 ???
unsigned int ptr = 0;
while (ptr != 11) {
if (pkt6[ptr] != my6[ptr])
break;
++ptr;
}
- if (ptr == 11) { // /88 matches an assigned address on this network
- const Address atPeer(pkt6 + ptr,5);
- if (atPeer != RR->identity.address()) {
- const MAC atPeerMac(atPeer,network->id());
- TRACE("ZT-RFC4193 NDP emulation: %.16llx: forging response for %s/%s",network->id(),atPeer.toString().c_str(),atPeerMac.toString().c_str());
-
- uint8_t adv[72];
- adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
- adv[4] = 0x00; adv[5] = 0x20;
- adv[6] = 0x3a; adv[7] = 0xff;
- for(int i=0;i<16;++i) adv[8 + i] = pkt6[i];
- for(int i=0;i<16;++i) adv[24 + i] = my6[i];
- adv[40] = 0x88; adv[41] = 0x00;
- adv[42] = 0x00; adv[43] = 0x00; // future home of checksum
- adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
- for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
- adv[64] = 0x02; adv[65] = 0x01;
- adv[66] = atPeerMac[0]; adv[67] = atPeerMac[1]; adv[68] = atPeerMac[2]; adv[69] = atPeerMac[3]; adv[70] = atPeerMac[4]; adv[71] = atPeerMac[5];
-
- uint16_t pseudo_[36];
- uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
- for(int i=0;i<32;++i) pseudo[i] = adv[8 + i];
- pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20;
- pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a;
- for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i];
- uint32_t checksum = 0;
- for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]);
- while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16);
- checksum = ~checksum;
- adv[42] = (checksum >> 8) & 0xff;
- adv[43] = checksum & 0xff;
-
- RR->node->putFrame(network->id(),network->userPtr(),atPeerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
- return; // stop processing: we have handled this frame with a spoofed local reply so no need to send it anywhere
+ if (ptr == 11) { // prefix match!
+ v6EmbeddedAddress.setTo(pkt6 + ptr,5);
+ break;
+ }
+ } else if (sipNetmaskBits == 40) { // ZT-6PLANE /40 ???
+ const uint32_t nwid32 = (uint32_t)((network->id() ^ (network->id() >> 32)) & 0xffffffff);
+ if ( (my6[0] == 0xfc) && (my6[1] == (uint8_t)((nwid32 >> 24) & 0xff)) && (my6[2] == (uint8_t)((nwid32 >> 16) & 0xff)) && (my6[3] == (uint8_t)((nwid32 >> 8) & 0xff)) && (my6[4] == (uint8_t)(nwid32 & 0xff))) {
+ unsigned int ptr = 0;
+ while (ptr != 5) {
+ if (pkt6[ptr] != my6[ptr])
+ break;
+ ++ptr;
+ }
+ if (ptr == 5) { // prefix match!
+ v6EmbeddedAddress.setTo(pkt6 + ptr,5);
+ break;
}
}
}
}
}
- }
+
+ if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
+ const MAC peerMac(v6EmbeddedAddress,network->id());
+ TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
+
+ uint8_t adv[72];
+ adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
+ adv[4] = 0x00; adv[5] = 0x20;
+ adv[6] = 0x3a; adv[7] = 0xff;
+ for(int i=0;i<16;++i) adv[8 + i] = pkt6[i];
+ for(int i=0;i<16;++i) adv[24 + i] = my6[i];
+ adv[40] = 0x88; adv[41] = 0x00;
+ adv[42] = 0x00; adv[43] = 0x00; // future home of checksum
+ adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
+ for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
+ adv[64] = 0x02; adv[65] = 0x01;
+ adv[66] = peerMac[0]; adv[67] = peerMac[1]; adv[68] = peerMac[2]; adv[69] = peerMac[3]; adv[70] = peerMac[4]; adv[71] = peerMac[5];
+
+ uint16_t pseudo_[36];
+ uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
+ for(int i=0;i<32;++i) pseudo[i] = adv[8 + i];
+ pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20;
+ pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a;
+ for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i];
+ uint32_t checksum = 0;
+ for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]);
+ while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16);
+ checksum = ~checksum;
+ adv[42] = (checksum >> 8) & 0xff;
+ adv[43] = checksum & 0xff;
+
+ RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
+ return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query.
+ } // else no NDP emulation
+ } // else no NDP emulation
}
/* Learn multicast groups for bridged-in hosts.
@@ -830,7 +849,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
tmp.setFragmented(chunkSize < tmp.size());
- tmp.armor(peer->key(),encrypt);
+ const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
+ if (trustedPathId) {
+ tmp.setTrusted(trustedPathId);
+ } else {
+ tmp.armor(peer->key(),encrypt);
+ }
if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
if (chunkSize < tmp.size()) {
diff --git a/zerotierone/node/Topology.cpp b/zerotierone/node/Topology.cpp
index 4105eae..6e96f2e 100644
--- a/zerotierone/node/Topology.cpp
+++ b/zerotierone/node/Topology.cpp
@@ -44,6 +44,7 @@ static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x0
Topology::Topology(const RuntimeEnvironment *renv) :
RR(renv),
+ _trustedPathCount(0),
_amRoot(false)
{
std::string alls(RR->node->dataStoreGet("peers.save"));
diff --git a/zerotierone/node/Topology.hpp b/zerotierone/node/Topology.hpp
index 86fbb01..03c491e 100644
--- a/zerotierone/node/Topology.hpp
+++ b/zerotierone/node/Topology.hpp
@@ -28,6 +28,7 @@
#include <utility>
#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
#include "Address.hpp"
#include "Identity.hpp"
@@ -252,12 +253,64 @@ public:
*/
inline bool amRoot() const throw() { return _amRoot; }
+ /**
+ * Get the outbound trusted path ID for a physical address, or 0 if none
+ *
+ * @param physicalAddress Physical address to which we are sending the packet
+ * @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
+ */
+ inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
+ {
+ for(unsigned int i=0;i<_trustedPathCount;++i) {
+ if (_trustedPathNetworks[i].containsAddress(physicalAddress))
+ return _trustedPathIds[i];
+ }
+ return 0;
+ }
+
+ /**
+ * Check whether in incoming trusted path marked packet is valid
+ *
+ * @param physicalAddress Originating physical address
+ * @param trustedPathId Trusted path ID from packet (from MAC field)
+ */
+ inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
+ {
+ for(unsigned int i=0;i<_trustedPathCount;++i) {
+ if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set trusted paths in this topology
+ *
+ * @param networks Array of networks (prefix/netmask bits)
+ * @param ids Array of trusted path IDs
+ * @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
+ */
+ inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
+ {
+ if (count > ZT_MAX_TRUSTED_PATHS)
+ count = ZT_MAX_TRUSTED_PATHS;
+ Mutex::Lock _l(_lock);
+ for(unsigned int i=0;i<count;++i) {
+ _trustedPathIds[i] = ids[i];
+ _trustedPathNetworks[i] = networks[i];
+ }
+ _trustedPathCount = count;
+ }
+
private:
Identity _getIdentity(const Address &zta);
void _setWorld(const World &newWorld);
const RuntimeEnvironment *const RR;
+ uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
+ InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
+ unsigned int _trustedPathCount;
World _world;
Hashtable< Address,SharedPtr<Peer> > _peers;
std::vector< Address > _rootAddresses;
diff --git a/zerotierone/node/Utils.cpp b/zerotierone/node/Utils.cpp
index 00aeea3..2d9515e 100644
--- a/zerotierone/node/Utils.cpp
+++ b/zerotierone/node/Utils.cpp
@@ -262,6 +262,24 @@ std::vector<std::string> Utils::split(const char *s,const char *const sep,const
return fields;
}
+bool Utils::scopy(char *dest,unsigned int len,const char *src)
+{
+ if (!len)
+ return false; // sanity check
+ if (!src) {
+ *dest = (char)0;
+ return true;
+ }
+ char *end = dest + len;
+ while ((*dest++ = *src++)) {
+ if (dest == end) {
+ *(--dest) = (char)0;
+ return false;
+ }
+ }
+ return true;
+}
+
unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
throw(std::length_error)
{
diff --git a/zerotierone/node/Utils.hpp b/zerotierone/node/Utils.hpp
index 3f4cc76..cfe5650 100644
--- a/zerotierone/node/Utils.hpp
+++ b/zerotierone/node/Utils.hpp
@@ -49,7 +49,6 @@ public:
* @return True if strings are equal
*/
static inline bool secureEq(const void *a,const void *b,unsigned int len)
- throw()
{
uint8_t diff = 0;
for(unsigned int i=0;i<len;++i)
@@ -225,27 +224,17 @@ public:
}
/**
- * Perform a safe C string copy
+ * Perform a safe C string copy, ALWAYS null-terminating the result
*
- * @param dest Destination buffer
- * @param len Length of buffer
- * @param src Source string
+ * This will never ever EVER result in dest[] not being null-terminated
+ * regardless of any input parameter (other than len==0 which is invalid).
+ *
+ * @param dest Destination buffer (must not be NULL)
+ * @param len Length of dest[] (if zero, false is returned and nothing happens)
+ * @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
* @return True on success, false on overflow (buffer will still be 0-terminated)
*/
- static inline bool scopy(char *dest,unsigned int len,const char *src)
- throw()
- {
- if (!len)
- return false; // sanity check
- char *end = dest + len;
- while ((*dest++ = *src++)) {
- if (dest == end) {
- *(--dest) = (char)0;
- return false;
- }
- }
- return true;
- }
+ static bool scopy(char *dest,unsigned int len,const char *src);
/**
* Variant of snprintf that is portable and throws an exception
@@ -269,7 +258,6 @@ public:
* @return Number of bits set in this integer (0-32)
*/
static inline uint32_t countBits(uint32_t v)
- throw()
{
v = v - ((v >> 1) & (uint32_t)0x55555555);
v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333);
@@ -284,7 +272,6 @@ public:
* @return True if memory is all zero
*/
static inline bool isZero(const void *p,unsigned int len)
- throw()
{
for(unsigned int i=0;i<len;++i) {
if (((const unsigned char *)p)[i])