From b9dba97fdb8644e3c427b079d81ed9db3697bf1b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 26 Apr 2016 17:11:25 -0700 Subject: [PATCH] Bunch more refactoring for an even more compact NetworkConfig representation, especially rules. --- include/ZeroTierOne.h | 163 ++++++--------- node/Address.hpp | 7 + node/NetworkConfig.cpp | 125 ++++-------- node/NetworkConfig.hpp | 439 +++++++++++++++++++++++++++++++++++++---- node/Node.cpp | 20 +- node/Switch.cpp | 11 +- node/Topology.cpp | 7 +- 7 files changed, 524 insertions(+), 248 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 8dd5962af..2237b30dc 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -84,12 +84,12 @@ extern "C" { /** * Maximum number of local routes on a network */ -#define ZT_MAX_NETWORK_LOCAL_ROUTES 32 +#define ZT_MAX_NETWORK_LOCAL_ROUTES 16 /** * Maximum number of statically assigned IP addresses per network endpoint using ZT address management (not DHCP) */ -#define ZT_MAX_ZT_ASSIGNED_ADDRESSES 32 +#define ZT_MAX_ZT_ASSIGNED_ADDRESSES 16 /** * Maximum number of default routes / gateways on a network (ZT managed) @@ -97,14 +97,14 @@ extern "C" { #define ZT_MAX_NETWORK_GATEWAYS 8 /** - * Maximum number of active bridges on a network + * Maximum number of "specialists" on a network -- bridges, relays, etc. */ -#define ZT_MAX_NETWORK_ACTIVE_BRIDGES 256 +#define ZT_MAX_NETWORK_SPECIALISTS 256 /** - * Maximum number of static devices on a network + * Maximum number of static physical to ZeroTier address mappings (typically relays, etc.) */ -#define ZT_MAX_NETWORK_STATIC_DEVICES 32 +#define ZT_MAX_NETWORK_STATIC_PHYSICAL_ADDRESSES 16 /** * Maximum number of rule table entries per network (can be increased) @@ -392,30 +392,6 @@ enum ZT_VirtualNetworkStatus ZT_NETWORK_STATUS_CLIENT_TOO_OLD = 5 }; -/** - * A network-scope defined static device entry - * - * Statically defined devices can have pre-specified endpoint addresses - * and can serve as things like network-specific relays. - */ -typedef struct -{ - /** - * ZeroTier address (least significant 40 bits, other bits ignored) - */ - uint64_t address; - - /** - * Physical address or zero ss_family if unspecified (two entries to support both V4 and V6) - */ - struct sockaddr_storage physical[2]; - - /** - * Flags indicating roles (if any) and restrictions - */ - unsigned int flags; -} ZT_VirtualNetworkStaticDevice; - /** * Virtual network type codes */ @@ -433,9 +409,13 @@ enum ZT_VirtualNetworkType }; /** - * An action in a network rule + * The type of a virtual network rules table entry + * + * These must range from 0 to 127 (0x7f). + * + * Each rule is composed of one or more MATCHes followed by an ACTION. */ -enum ZT_VirtualNetworkRuleAction +enum ZT_VirtualNetworkRuleType { /** * Drop frame @@ -455,108 +435,97 @@ enum ZT_VirtualNetworkRuleAction /** * Redirect frame to ZeroTier device in datum.zt[1] regardless of Ethernet addressing or anything else */ - ZT_NETWORK_RULE_ACTION_REDIRECT = 3 -}; - -/** - * Datum type (variant) that a rule matches - */ -enum ZT_VirtualNetworkRuleMatches -{ - /** - * Matches all packets (no criteria) - */ - ZT_NETWORK_RULE_MATCHES_ALL = 0, + ZT_NETWORK_RULE_ACTION_REDIRECT = 3, /** * Source ZeroTier address -- analogous to an Ethernet port ID on a switch */ - ZT_NETWORK_RULE_MATCHES_SOURCE_ZEROTIER_ADDRESS = 1, + ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS = 32, /** * Destination ZeroTier address -- analogous to an Ethernet port ID on a switch */ - ZT_NETWORK_RULE_MATCHES_DEST_ZEROTIER_ADDRESS = 2, + ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS = 33, /** * Ethernet VLAN ID */ - ZT_NETWORK_RULE_MATCHES_VLAN_ID = 3, + ZT_NETWORK_RULE_MATCH_VLAN_ID = 34, /** * Ethernet VLAN PCP */ - ZT_NETWORK_RULE_MATCHES_VLAN_PCP = 4, + ZT_NETWORK_RULE_MATCH_VLAN_PCP = 35, /** * Ethernet VLAN DEI */ - ZT_NETWORK_RULE_MATCHES_VLAN_DEI = 5, + ZT_NETWORK_RULE_MATCH_VLAN_DEI = 36, /** * Ethernet frame type */ - ZT_NETWORK_RULE_MATCHES_ETHERTYPE = 6, + ZT_NETWORK_RULE_MATCH_ETHERTYPE = 37, /** * Source Ethernet MAC address */ - ZT_NETWORK_RULE_MATCHES_MAC_SOURCE = 7, + ZT_NETWORK_RULE_MATCH_MAC_SOURCE = 38, /** * Destination Ethernet MAC address */ - ZT_NETWORK_RULE_MATCHES_MAC_DEST = 8, + ZT_NETWORK_RULE_MATCH_MAC_DEST = 39, /** * Source IPv4 address */ - ZT_NETWORK_RULE_MATCHES_IPV4_SOURCE = 9, + ZT_NETWORK_RULE_MATCH_IPV4_SOURCE = 40, /** * Destination IPv4 address */ - ZT_NETWORK_RULE_MATCHES_IPV4_DEST = 10, + ZT_NETWORK_RULE_MATCH_IPV4_DEST = 41, /** * Source IPv6 address */ - ZT_NETWORK_RULE_MATCHES_IPV6_SOURCE = 11, + ZT_NETWORK_RULE_MATCH_IPV6_SOURCE = 42, /** * Destination IPv6 address */ - ZT_NETWORK_RULE_MATCHES_IPV6_DEST = 12, + ZT_NETWORK_RULE_MATCH_IPV6_DEST = 43, /** * IP TOS (type of service) */ - ZT_NETWORK_RULE_MATCHES_IP_TOS = 13, + ZT_NETWORK_RULE_MATCH_IP_TOS = 44, /** * IP protocol */ - ZT_NETWORK_RULE_MATCHES_IP_PROTOCOL = 14, + ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 45, /** * IP source port range (start-end, inclusive) */ - ZT_NETWORK_RULE_MATCHES_IP_SOURCE_PORT_RANGE = 15, + ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 46, /** * IP destination port range (start-end, inclusive) */ - ZT_NETWORK_RULE_MATCHES_IP_DEST_PORT_RANGE = 16, + ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 47, /** - * Packet characteristic flags + * Packet boolean characteristics */ - ZT_NETWORK_RULE_MATCHES_FLAGS = 17, + ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 48, /** * Frame size range (start-end, inclusive) */ - ZT_NETWORK_RULE_MATCHES_FRAME_SIZE_RANGE = 18 + ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 49 }; /** @@ -565,53 +534,48 @@ enum ZT_VirtualNetworkRuleMatches * NOTE: Currently (1.1.x) only etherType is supported! Other things will * have no effect until the rules engine is fully implemented. * - * Multiple entries in the table can have the same ruleNo. This indicates - * a row with multiple matching criteria. - * - * This gives the table a much more space-efficient compressed representation, - * allowing far more rules to be efficiently sent in small netconf structures. + * Rules are stored in a table in which one or more match entries is followed + * by an action. If more than one match precedes an action */ typedef struct { - /** - * Rule number and sort order - * - * Multiple entries in the table can have the same ruleNo. This causes them - * to be matched as an AND together, e.g. both IP source and IP source port. - */ - uint16_t ruleNo; - - /** - * Field that this rules table entry matches (enum ZT_VirtualNetworkRuleMatches) - */ - uint8_t matches; - /** - * Action if rule matches (enum ZT_VirtualNetworkRuleAction) + * Least significant 7 bits: ZT_VirtualNetworkRuleType, most significant 1 bit is NOT bit + * + * If the NOT bit is set, then matches will be interpreted as "does not + * match." The NOT bit has no effect on actions. + * + * Use "& 0x7f" to get the enum and "& 0x80" to get the NOT flag. + * + * This is essentially a variant selector determining which field of 'v' is + * used and its meaning. */ - uint8_t action; + uint8_t t; /** - * Union containing the datum for this rule - * - * The rule entry functions like a variant type, with the field of datum - * that is relevant/valid determined by the 'matches' enum. + * Union containing the value of this rule -- which field is used depends on 't' */ union { /** - * IPv6 address in big-endian / network byte order + * IPv6 address in big-endian / network byte order and netmask bits */ - uint8_t ipv6[16]; - - /** - * Flags (128 possible) - */ - uint8_t flags[16]; + struct { + uint8_t ip[16]; + uint8_t mask; + } ipv6; /** * IPv4 address in big-endian / network byte order */ - uint32_t ipv4; + struct { + uint32_t ip; + uint8_t mask; + } ipv4; + + /** + * Packet characteristic flags being matched + */ + uint64_t characteristics; /** * IP port range -- start-end inclusive -- host byte order @@ -619,12 +583,9 @@ typedef struct uint16_t port[2]; /** - * Two possible 40-bit ZeroTier addresses in host byte order (least significant 40 bits of uint64_t) - * - * The first of these ([0]) is used in most cases e.g. matching ZT source - * address. The second is used as the observer for the TEE action. + * 40-bit ZeroTier address (in least significant bits, host byte order) */ - uint64_t zt[2]; + uint64_t zt; /** * 48-bit Ethernet MAC address in big-endian order @@ -665,7 +626,7 @@ typedef struct * Ethernet packet size in host byte order (start-end, inclusive) */ uint16_t frameSize[2]; - } datum; + } v; } ZT_VirtualNetworkRule; /** diff --git a/node/Address.hpp b/node/Address.hpp index db46a4c93..9bf5605a2 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -216,6 +216,13 @@ public: */ inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); } + inline bool operator==(const uint64_t &a) const throw() { return (_a == (a & 0xffffffffffULL)); } + inline bool operator!=(const uint64_t &a) const throw() { return (_a != (a & 0xffffffffffULL)); } + inline bool operator>(const uint64_t &a) const throw() { return (_a > (a & 0xffffffffffULL)); } + inline bool operator<(const uint64_t &a) const throw() { return (_a < (a & 0xffffffffffULL)); } + inline bool operator>=(const uint64_t &a) const throw() { return (_a >= (a & 0xffffffffffULL)); } + inline bool operator<=(const uint64_t &a) const throw() { return (_a <= (a & 0xffffffffffULL)); } + inline bool operator==(const Address &a) const throw() { return (_a == a._a); } inline bool operator!=(const Address &a) const throw() { return (_a != a._a); } inline bool operator>(const Address &a) const throw() { return (_a > a._a); } diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 391d83cae..66c1c3ee4 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -23,60 +23,6 @@ namespace ZeroTier { -namespace { - -struct ZT_VirtualNetworkStaticDevice_SortByAddress -{ - inline bool operator()(const ZT_VirtualNetworkStaticDevice &a,const ZT_VirtualNetworkStaticDevice &b) - { - return (a.address < b.address); - } -}; - -struct ZT_VirtualNetworkRule_SortByRuleNo -{ - inline bool operator()(const ZT_VirtualNetworkRule &a,const ZT_VirtualNetworkRule &b) - { - return (a.ruleNo < b.ruleNo); - } -}; - -} // anonymous namespace - -NetworkConfig NetworkConfig::createTestNetworkConfig(const Address &self) -{ - NetworkConfig nc; - - nc._nwid = ZT_TEST_NETWORK_ID; - nc._timestamp = 1; - nc._revision = 1; - nc._issuedTo = self; - nc._multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT; - nc._flags = ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; - nc._type = ZT_NETWORK_TYPE_PUBLIC; - - nc._rules[nc._ruleCount].ruleNo = 1; - nc._rules[nc._ruleCount].matches = (uint8_t)ZT_NETWORK_RULE_MATCHES_ALL; - nc._rules[nc._ruleCount].action = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; - nc._ruleCount = 1; - - Utils::snprintf(nc._name,sizeof(nc._name),"ZT_TEST_NETWORK"); - - // Make up a V4 IP from 'self' in the 10.0.0.0/8 range -- no - // guarantee of uniqueness but collisions are unlikely. - uint32_t ip = (uint32_t)((self.toInt() & 0x00ffffff) | 0x0a000000); // 10.x.x.x - if ((ip & 0x000000ff) == 0x000000ff) ip ^= 0x00000001; // but not ending in .255 - if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0 - nc._staticIps[0] = InetAddress(Utils::hton(ip),8); - - // Assign an RFC4193-compliant IPv6 address -- will never collide - nc._staticIps[1] = InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt()); - - nc._staticIpCount = 2; - - return nc; -} - #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF void NetworkConfig::fromDictionary(const char *ds,unsigned int dslen) @@ -116,12 +62,19 @@ void NetworkConfig::fromDictionary(const char *ds,unsigned int dslen) if (a->length() == ZT_ADDRESS_LENGTH_HEX) { // ignore empty or garbage fields Address tmp(*a); if (!tmp.isReserved()) { - if ((_activeBridgeCount < ZT_MAX_NETWORK_ACTIVE_BRIDGES)&&(std::find(&(_activeBridges[0]),&(_activeBridges[_activeBridgeCount]),tmp) == &(_activeBridges[_activeBridgeCount]))) - _activeBridges[_activeBridgeCount++] = tmp; + 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::sort(&(_activeBridges[0]),&(_activeBridges[_activeBridgeCount])); std::string ipAddrs(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC,std::string())); { @@ -169,49 +122,55 @@ void NetworkConfig::fromDictionary(const char *ds,unsigned int dslen) std::vector relaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS,"").c_str(),",","","")); for(std::vector::const_iterator r(relaysSplit.begin());r!=relaysSplit.end();++r) { if (r->length() >= ZT_ADDRESS_LENGTH_HEX) { - Address addr(r->substr(0,ZT_ADDRESS_LENGTH_HEX).c_str()); - InetAddress phys[2]; - unsigned int physCount = 0; + 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 phySplit(Utils::split(r->substr(semi+1).c_str(),",","","")); - for(std::vector::const_iterator p(phySplit.begin());((p!=phySplit.end())&&(physCount < 2));++p) { - phys[physCount] = InetAddress(*p); - if (phys[physCount]) - ++physCount; - else phys[physCount].zero(); + for(std::vector::const_iterator p(phySplit.begin());((p!=phySplit.end())&&(phyCount < 2));++p) { + phy[phyCount] = InetAddress(*p); + if (phy[phyCount]) + ++phyCount; + else phy[phyCount].zero(); } } - unsigned int p = _staticCount; - for(unsigned int i=0;i<_staticCount;++i) { - if (_static[p].address == addr.toInt()) { - p = i; + 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 ((p == _staticCount)&&(_staticCount < ZT_MAX_NETWORK_STATIC_DEVICES)) + + if ((specialist)&&(_specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) + _specialists[_specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY; + + if ((phy[0])&&(_staticCount < ZT_MAX_NETWORK_STATIC_PHYSICAL_ADDRESSES)) { + _static[_staticCount].zt = zt; + _static[_staticCount].phy = phy[0]; + ++_staticCount; + } + if ((phy[1])&&(_staticCount < ZT_MAX_NETWORK_STATIC_PHYSICAL_ADDRESSES)) { + _static[_staticCount].zt = zt; + _static[_staticCount].phy = phy[0]; ++_staticCount; - if (p < ZT_MAX_NETWORK_STATIC_DEVICES) { - _static[p].address = Address(r->c_str()); - for(unsigned int i=0;i ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","","")); - int rno = 0; for(std::vector::const_iterator et(ets.begin());et!=ets.end();++et) { unsigned int et2 = Utils::hexStrToUInt(et->c_str()) & 0xffff; - if (_ruleCount < ZT_MAX_NETWORK_RULES) { - memset(&(_rules[_ruleCount]),0,sizeof(ZT_VirtualNetworkRule)); - _rules[_ruleCount].ruleNo = rno; rno += 10; - _rules[_ruleCount].matches = (uint8_t)((et2 == 0) ? ZT_NETWORK_RULE_MATCHES_ALL : ZT_NETWORK_RULE_MATCHES_ETHERTYPE); - _rules[_ruleCount].action = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT; - _rules[_ruleCount].datum.etherType = (uint16_t)et2; + 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; + } + _rules[_ruleCount].t = ZT_NETWORK_RULE_ACTION_ACCEPT; ++_ruleCount; } } diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index c0ca02308..6e91cb9d0 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -23,10 +23,8 @@ #include #include -#include #include #include -#include #include "../include/ZeroTierOne.h" @@ -59,6 +57,21 @@ */ #define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0002 +/** + * Device is a network preferred relay + */ +#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY 0x0000010000000000ULL + +/** + * Device is an active bridge + */ +#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL + +/** + * This device is allowed to send packets from any Ethernet MAC, including ZeroTier-reserved ones + */ +#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_IMPOSTOR 0x0000040000000000ULL + namespace ZeroTier { #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF @@ -119,6 +132,15 @@ namespace ZeroTier { class NetworkConfig { public: + /** + * Network preferred relay with optional physical endpoint addresses + */ + struct Relay + { + Address address; + InetAddress phy4,phy6; + }; + /** * Create an instance of a NetworkConfig for the test network ID * @@ -128,7 +150,37 @@ public: * @param self This node's ZT address * @return Configuration for test network ID */ - static NetworkConfig createTestNetworkConfig(const Address &self); + static inline NetworkConfig createTestNetworkConfig(const Address &self) + { + NetworkConfig nc; + + nc._nwid = ZT_TEST_NETWORK_ID; + nc._timestamp = 1; + nc._revision = 1; + nc._issuedTo = self; + nc._multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT; + nc._flags = ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; + nc._type = ZT_NETWORK_TYPE_PUBLIC; + + nc._rules[nc._ruleCount].t = ZT_NETWORK_RULE_ACTION_ACCEPT; + nc._ruleCount = 1; + + Utils::snprintf(nc._name,sizeof(nc._name),"ZT_TEST_NETWORK"); + + // Make up a V4 IP from 'self' in the 10.0.0.0/8 range -- no + // guarantee of uniqueness but collisions are unlikely. + uint32_t ip = (uint32_t)((self.toInt() & 0x00ffffff) | 0x0a000000); // 10.x.x.x + if ((ip & 0x000000ff) == 0x000000ff) ip ^= 0x00000001; // but not ending in .255 + if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0 + nc._staticIps[0] = InetAddress(Utils::hton(ip),8); + + // Assign an RFC4193-compliant IPv6 address -- will never collide + nc._staticIps[1] = InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt()); + + nc._staticIpCount = 2; + + return nc; + } NetworkConfig() { @@ -152,28 +204,20 @@ public: */ inline bool permitsEtherType(unsigned int etherType) const { + unsigned int et = 0; for(unsigned int i=0;i<_ruleCount;++i) { - if ((ZT_VirtualNetworkRuleMatches)_rules[i].matches == ZT_NETWORK_RULE_MATCHES_ETHERTYPE) { - if (_rules[i].datum.etherType == etherType) - return ((ZT_VirtualNetworkRuleAction)_rules[i].action == ZT_NETWORK_RULE_ACTION_ACCEPT); - } else if ((ZT_VirtualNetworkRuleMatches)_rules[i].matches == ZT_NETWORK_RULE_MATCHES_ALL) { - return ((ZT_VirtualNetworkRuleAction)_rules[i].action == ZT_NETWORK_RULE_ACTION_ACCEPT); + 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 ((!et)||(et == etherType)) + return true; + et = 0; } } return false; } -#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF - /** - * Parse an old-style dictionary and fill in structure - * - * @param ds String-serialized dictionary - * @param dslen Length of dictionary in bytes - * @throws std::invalid_argument Invalid dictionary - */ - void fromDictionary(const char *ds,unsigned int dslen); -#endif - /** * @return Network ID that this config applies to */ @@ -273,33 +317,47 @@ public: inline std::vector
activeBridges() const { std::vector
r; - for(unsigned int i=0;i<_activeBridgeCount;++i) - r.push_back(_activeBridges[i]); - return r; - } - - /** - * @return Network-preferred relays for this network (if none, only roots will be used) - */ - inline std::vector relays() const - { - std::vector r; - for(unsigned int i=0;i<_staticCount;++i) { - if ((_static[i].flags & ZT_NETWORK_STATIC_DEVICE_IS_RELAY) != 0) - r.push_back(_static[i]); + for(unsigned int i=0;i<_specialistCount;++i) { + if ((_specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) + r.push_back(Address(_specialists[i])); } return r; } /** - * @return Static device at index [i] (warning: no bounds checking! see staticDeviceCount() for count) + * Look up a static physical address for a given ZeroTier address + * + * @param zt ZeroTier address + * @param af Address family (e.g. AF_INET) or 0 for the first we find of any type + * @return Physical address, if any */ - const ZT_VirtualNetworkStaticDevice &staticDevice(unsigned int i) const { return _static[i]; } + inline InetAddress staticPhysicalAddress(const Address &zt,unsigned int af) const + { + for(unsigned int i=0;i<_staticCount;++i) { + if (_static[i].zt == zt) { + if ((af == 0)||((unsigned int)_static[i].phy.ss_family == af)) + return _static[i].phy; + } + } + return InetAddress(); + } /** - * @return Number of static devices defined in this network config + * @return Network-preferred relays for this network (if none, only roots will be used) */ - unsigned int staticDeviceCount() const { return _staticCount; } + inline std::vector relays() const + { + std::vector r; + for(unsigned int i=0;i<_specialistCount;++i) { + if ((_specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) { + r.push_back(Relay()); + r.back().address = _specialists[i]; + r.back().phy4 = staticPhysicalAddress(r.back().address,AF_INET); + r.back().phy6 = staticPhysicalAddress(r.back().address,AF_INET6); + } + } + return r; + } /** * @param fromPeer Peer attempting to bridge other Ethernet peers onto network @@ -309,8 +367,38 @@ public: { if ((_flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0) return true; - for(unsigned int i=0;i<_activeBridgeCount;++i) { - if (_activeBridges[i] == fromPeer) + for(unsigned int i=0;i<_specialistCount;++i) { + if ((fromPeer == _specialists[i])&&((_specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)) + return true; + } + return false; + } + + /** + * Iterate through relays efficiently + * + * @param ptr Value-result parameter -- start by initializing with zero, then call until return is null + * @return Address of relay or NULL if no more + */ + Address nextRelay(unsigned int &ptr) const + { + while (ptr < _specialistCount) { + if ((_specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) { + return Address(_specialists[ptr]); + } + ++ptr; + } + return Address(); + } + + /** + * @param zt ZeroTier address + * @return True if this address is a relay + */ + bool isRelay(const Address &zt) const + { + for(unsigned int i=0;i<_specialistCount;++i) { + if ((zt == _specialists[i])&&((_specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0)) return true; } return false; @@ -324,6 +412,263 @@ 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 + inline void serialize(Buffer &b) const + { + b.append((uint8_t)ZT_NETWORKCONFIG_V2_MARKER_BYTE); + + b.append((uint16_t)0); // version + + b.append((uint64_t)_nwid); + 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)_localRouteCount); + for(unsigned int i=0;i<_localRouteCount;++i) + _localRoutes[i].serialize(b); + + b.append((uint16_t)_staticIpCount); + for(unsigned int i=0;i<_staticIpCount;++i) + _staticIps[i].serialize(b); + + b.append((uint16_t)_gatewayCount); + for(unsigned int i=0;i<_gatewayCount;++i) + _gateways[i].serialize(b); + + b.append((uint16_t)_staticCount); + for(unsigned int i=0;i<_staticCount;++i) { + _static[i].zt.appendTo(b); + _static[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; + } + } + + b.append((uint16_t)0); // extended bytes, currently 0 since unused + } + + template + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + { + memset(this,0,sizeof(NetworkConfig)); + + unsigned int p = startAt; + + if (b[p++] != ZT_NETWORKCONFIG_V2_MARKER_BYTE) + throw std::invalid_argument("use fromDictionary() for old style netconf deserialization"); + if (b.template at(p) != 0) + throw std::invalid_argument("unrecognized version"); + p += 2; + + _nwid = b.template at(p); p += 8; + _timestamp = b.template at(p); p += 8; + _revision = b.template at(p); p += 8; + _issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH; + _multicastLimit = (unsigned int)b.template at(p); p += 4; + _flags = (unsigned int)b.template at(p); p += 4; + _type = (ZT_VirtualNetworkType)b[p++]; + + unsigned int nl = (unsigned int)b[p++]; + if (nl > ZT_MAX_NETWORK_SHORT_NAME_LENGTH) + nl = ZT_MAX_NETWORK_SHORT_NAME_LENGTH; + memcpy(_name,b.field(p,nl),nl); + // _name will always be null terminated since field size is ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1 + + _specialistCount = (unsigned int)b.template at(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(p); p += 8; + } + + _localRouteCount = (unsigned int)b.template at(p); p += 2; + if (_localRouteCount > ZT_MAX_NETWORK_LOCAL_ROUTES) + throw std::invalid_argument("overflow (local routes)"); + for(unsigned int i=0;i<_localRouteCount;++i) { + p += _localRoutes[i].deserialize(b,p); + } + + _staticIpCount = (unsigned int)b.template at(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); + } + + _gatewayCount = (unsigned int)b.template at(p); p += 2; + if (_gatewayCount > ZT_MAX_NETWORK_GATEWAYS) + throw std::invalid_argument("overflow (gateways)"); + for(unsigned int i=0;i<_gatewayCount;++i) { + p += _gateways[i].deserialize(b,p); + } + + _staticCount = (unsigned int)b.template at(p); p += 2; + if (_staticCount > ZT_MAX_NETWORK_STATIC_PHYSICAL_ADDRESSES) + throw std::invalid_argument("overflow (static addresses)"); + for(unsigned int i=0;i<_staticCount;++i) { + } + + _ruleCount = (unsigned int)b.template at(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(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(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(p); + _rules[i].v.port[1] = b.template at(p+2); + break; + case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: + _rules[i].v.characteristics = b.template at(p); + break; + case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: + _rules[i].v.frameSize[0] = b.template at(p); + _rules[i].v.frameSize[1] = b.template at(p+2); + break; + } + p += rlen; + } + + p += b.template at(p) + 2; + + return (p - startAt); + } + +#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF + void fromDictionary(const char *ds,unsigned int dslen); +#endif + protected: // protected so that a subclass can fill this out in network controller code uint64_t _nwid; uint64_t _timestamp; @@ -335,14 +680,24 @@ protected: // protected so that a subclass can fill this out in network controll char _name[ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1]; - Address _activeBridges[ZT_MAX_NETWORK_ACTIVE_BRIDGES]; + // Special ZeroTier addresses -- most significant 40 bits are address, least 24 are specialist type flags + uint64_t _specialists[ZT_MAX_NETWORK_SPECIALISTS]; + + // ZeroTier-managed IPs and routing table entries and stuff InetAddress _localRoutes[ZT_MAX_NETWORK_LOCAL_ROUTES]; InetAddress _staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES]; InetAddress _gateways[ZT_MAX_NETWORK_GATEWAYS]; - ZT_VirtualNetworkStaticDevice _static[ZT_MAX_NETWORK_STATIC_DEVICES]; + + // ZeroTier to physical static mappings + struct { + Address zt; + InetAddress phy; + } _static[ZT_MAX_NETWORK_STATIC_PHYSICAL_ADDRESSES]; + + // Network rules (only Ethernet type filtering is currently supported) ZT_VirtualNetworkRule _rules[ZT_MAX_NETWORK_RULES]; - unsigned int _activeBridgeCount; + unsigned int _specialistCount; unsigned int _localRouteCount; unsigned int _staticIpCount; unsigned int _gatewayCount; diff --git a/node/Node.cpp b/node/Node.cpp index 047511727..5d534239d 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -173,7 +173,7 @@ ZT_ResultCode Node::processVirtualNetworkFrame( class _PingPeersThatNeedPing { public: - _PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector &relays) : + _PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector &relays) : lastReceiveFromUpstream(0), RR(renv), _now(now), @@ -217,14 +217,10 @@ public: // Check for network preferred relays, also considered 'upstream' and thus always // pinged to keep links up. If they have stable addresses we will try them there. - for(std::vector::const_iterator r(_relays.begin());r!=_relays.end();++r) { - if (r->address == p->address().toInt()) { - for(unsigned int i=0;i<2;++i) { - if (r->physical[i].ss_family == AF_INET) - stableEndpoint4 = r->physical[i]; - else if (r->physical[i].ss_family == AF_INET6) - stableEndpoint6 = r->physical[i]; - } + for(std::vector::const_iterator r(_relays.begin());r!=_relays.end();++r) { + if (r->address == p->address()) { + stableEndpoint4 = r->phy4; + stableEndpoint6 = r->phy6; upstream = true; break; } @@ -271,7 +267,7 @@ public: private: const RuntimeEnvironment *RR; uint64_t _now; - const std::vector &_relays; + const std::vector &_relays; World _world; }; @@ -287,7 +283,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB _lastPingCheck = now; // Get relays and networks that need config without leaving the mutex locked - std::vector< ZT_VirtualNetworkStaticDevice > networkRelays; + std::vector< NetworkConfig::Relay > networkRelays; std::vector< SharedPtr > needConfig; { Mutex::Lock _l(_networks_m); @@ -296,7 +292,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB needConfig.push_back(n->second); } if (n->second->hasConfig()) { - std::vector r(n->second->config().relays()); + std::vector r(n->second->config().relays()); networkRelays.insert(networkRelays.end(),r.begin(),r.end()); } } diff --git a/node/Switch.cpp b/node/Switch.cpp index 9dbabdca7..3d74bddab 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -795,10 +795,11 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (!viaPath) { if (network) { unsigned int bestq = ~((unsigned int)0); // max unsigned int since quality is lower==better - for(unsigned int ri=0;riconfig().staticDeviceCount();++ri) { - const ZT_VirtualNetworkStaticDevice &r = network->config().staticDevice(ri); - if ((r.address != peer->address().toInt())&&((r.flags & ZT_NETWORK_STATIC_DEVICE_IS_RELAY) != 0)) { - SharedPtr rp(RR->topology->getPeer(Address(r.address))); + unsigned int ptr = 0; + for(;;) { + const Address raddr(network->config().nextRelay(ptr)); + if (raddr) { + SharedPtr rp(RR->topology->getPeer(raddr)); if (rp) { const unsigned int q = rp->relayQuality(now); if (q < bestq) { @@ -806,7 +807,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) rp.swap(relay); } } - } + } else break; } } diff --git a/node/Topology.cpp b/node/Topology.cpp index 21ed78a72..4105eae92 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -284,11 +284,8 @@ bool Topology::isUpstream(const Identity &id) const return true; std::vector< SharedPtr > nws(RR->node->allNetworks()); for(std::vector< SharedPtr >::const_iterator nw(nws.begin());nw!=nws.end();++nw) { - if ((*nw)->hasConfig()) { - for(unsigned int r=0;r<(*nw)->config().staticDeviceCount();++r) { - if ((((*nw)->config().staticDevice(r).flags & ZT_NETWORK_STATIC_DEVICE_IS_RELAY) != 0)&&((*nw)->config().staticDevice(r).address == id.address().toInt())) - return true; - } + if ((*nw)->config().isRelay(id.address())) { + return true; } } return false;