From d73607430131dd352cf9248f37e76c2618dd39e5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 22 Apr 2016 15:40:53 -0700 Subject: Refactor rules table in-memory structure in new NetworkConfig to permit far more rules with better space efficiency. --- include/ZeroTierOne.h | 219 +++++++++++++++++++++++++++++++++++++++---------- node/Constants.hpp | 3 + node/NetworkConfig.cpp | 25 ++---- node/NetworkConfig.hpp | 8 +- 4 files changed, 192 insertions(+), 63 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index b77cc820..75199195 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -107,9 +107,9 @@ extern "C" { #define ZT_MAX_NETWORK_STATIC_DEVICES 32 /** - * Maximum number of rules per network (can be increased) + * Maximum number of rule table entries per network (can be increased) */ -#define ZT_MAX_NETWORK_RULES 64 +#define ZT_MAX_NETWORK_RULES 256 /** * Maximum number of multicast group subscriptions per network @@ -432,102 +432,235 @@ enum ZT_VirtualNetworkType */ enum ZT_VirtualNetworkRuleAction { + /** + * Drop frame + */ ZT_NETWORK_RULE_ACTION_DROP = 0, - ZT_NETWORK_RULE_ACTION_ACCEPT = 1 + + /** + * Accept and pass frame + */ + ZT_NETWORK_RULE_ACTION_ACCEPT = 1, + + /** + * Forward a copy of this frame to an observer (in datum.zt[1]) + */ + ZT_NETWORK_RULE_ACTION_TEE = 2, + + /** + * Redirect frame to ZeroTier device in datum.zt[1] regardless of Ethernet addressing or anything else + */ + ZT_NETWORK_RULE_ACTION_REDIRECT = 3 }; /** - * Network flow rule - * - * Currently only etherType is supported! Other flags will have no effect - * until the rules engine is fully implemented. + * Datum type (variant) that a rule matches */ -typedef struct +enum ZT_VirtualNetworkRuleMatches { /** - * Rule sort order + * Matches all packets (no criteria) + */ + ZT_NETWORK_RULE_MATCHES_ALL = 0, + + /** + * Source ZeroTier address -- analogous to an Ethernet port ID on a switch */ - int ruleNo; + ZT_NETWORK_RULE_MATCHES_SOURCE_ZEROTIER_ADDRESS = 1, /** - * Source ZeroTier address ("port" on the global virtual switch) (0 == wildcard) + * Destination ZeroTier address -- analogous to an Ethernet port ID on a switch */ - uint64_t sourcePort; + ZT_NETWORK_RULE_MATCHES_DEST_ZEROTIER_ADDRESS = 2, /** - * Destination ZeroTier address ("port" on the global virtual switch) (0 == wildcard) + * Ethernet VLAN ID */ - uint64_t destPort; + ZT_NETWORK_RULE_MATCHES_VLAN_ID = 3, + + /** + * Ethernet VLAN PCP + */ + ZT_NETWORK_RULE_MATCHES_VLAN_PCP = 4, /** - * VLAN ID (-1 == wildcard) + * Ethernet VLAN DEI */ - int vlanId; + ZT_NETWORK_RULE_MATCHES_VLAN_DEI = 5, /** - * VLAN PCP (-1 == wildcard) + * Ethernet frame type */ - int vlanPcp; + ZT_NETWORK_RULE_MATCHES_ETHERTYPE = 6, /** - * Ethernet type (-1 == wildcard) + * Source Ethernet MAC address */ - int etherType; + ZT_NETWORK_RULE_MATCHES_MAC_SOURCE = 7, /** - * Source MAC address (least significant 48 bits, host byte order) (0 == wildcard) + * Destination Ethernet MAC address */ - uint64_t macSource; + ZT_NETWORK_RULE_MATCHES_MAC_DEST = 8, - /** - * Destination MAC address (least significant 48 bits, host byte order) (0 == wildcard) + /** + * Source IPv4 address + */ + ZT_NETWORK_RULE_MATCHES_IPV4_SOURCE = 9, + + /** + * Destination IPv4 address */ - uint64_t macDest; + ZT_NETWORK_RULE_MATCHES_IPV4_DEST = 10, /** - * Source IP address (ss_family == 0 for wildcard) + * Source IPv6 address */ - struct sockaddr_storage ipSource; + ZT_NETWORK_RULE_MATCHES_IPV6_SOURCE = 11, /** - * Destination IP address (ss_family == 0 for wildcard) + * Destination IPv6 address */ - struct sockaddr_storage ipDest; + ZT_NETWORK_RULE_MATCHES_IPV6_DEST = 12, /** - * IP type of service (-1 == wildcard) + * IP TOS (type of service) */ - int ipTos; + ZT_NETWORK_RULE_MATCHES_IP_TOS = 13, /** - * IP protocol (-1 == wildcard) + * IP protocol */ - int ipProtocol; + ZT_NETWORK_RULE_MATCHES_IP_PROTOCOL = 14, /** - * IP source port (-1 == wildcard) + * IP source port range (start-end, inclusive) */ - int ipSourcePort; + ZT_NETWORK_RULE_MATCHES_IP_SOURCE_PORT_RANGE = 15, /** - * IP destination port (-1 == wildcard) + * IP destination port range (start-end, inclusive) */ - int ipDestPort; + ZT_NETWORK_RULE_MATCHES_IP_DEST_PORT_RANGE = 16, /** - * Flags to match if set + * Packet characteristic flags */ - unsigned long flags; + ZT_NETWORK_RULE_MATCHES_FLAGS = 17, /** - * Flags to match if NOT set + * Frame size range (start-end, inclusive) */ - unsigned long invFlags; + ZT_NETWORK_RULE_MATCHES_FRAME_SIZE_RANGE = 18 +}; + +/** + * Network flow rule + * + * 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. + */ +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 + * Action if rule matches (enum ZT_VirtualNetworkRuleAction) */ - enum ZT_VirtualNetworkRuleAction action; + uint8_t action; + + /** + * 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 { + /** + * IPv6 address in big-endian / network byte order + */ + uint8_t ipv6[16]; + + /** + * Flags (128 possible) + */ + uint8_t flags[16]; + + /** + * IPv4 address in big-endian / network byte order + */ + uint32_t ipv4; + + /** + * IP port range -- start-end inclusive -- host byte order + */ + 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. + */ + uint64_t zt[2]; + + /** + * 48-bit Ethernet MAC address in big-endian order + */ + uint8_t mac[6]; + + /** + * VLAN ID in host byte order + */ + uint16_t vlanId; + + /** + * VLAN PCP (least significant 3 bits) + */ + uint8_t vlanPcp; + + /** + * VLAN DEI (single bit / boolean) + */ + uint8_t vlanDei; + + /** + * Ethernet type in host byte order + */ + uint16_t etherType; + + /** + * IP protocol + */ + uint8_t ipProtocol; + + /** + * IP type of service + */ + uint8_t ipTos; + + /** + * Ethernet packet size in host byte order (start-end, inclusive) + */ + uint16_t frameSize[2]; + } datum; } ZT_VirtualNetworkRule; /** diff --git a/node/Constants.hpp b/node/Constants.hpp index 4bca7d29..dc36b3a1 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -286,6 +286,9 @@ /** * Delay between requests for updated network autoconf information + * + * Don't lengthen this as it affects things like QoS / uptime monitoring + * via ZeroTier Central. This is the heartbeat, basically. */ #define ZT_NETWORK_AUTOCONF_DELAY 60000 diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index aab9a650..090648f8 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -56,16 +56,10 @@ NetworkConfig NetworkConfig::createTestNetworkConfig(const Address &self) nc._type = ZT_NETWORK_TYPE_PUBLIC; nc._enableBroadcast = true; - nc._rules[nc._ruleCount].ruleNo = 0; - nc._rules[nc._ruleCount].vlanId = -1; - nc._rules[nc._ruleCount].vlanPcp = -1; - nc._rules[nc._ruleCount].etherType = -1; - nc._rules[nc._ruleCount].ipTos = -1; - nc._rules[nc._ruleCount].ipProtocol = -1; - nc._rules[nc._ruleCount].ipSourcePort = -1; - nc._rules[nc._ruleCount].ipDestPort = -1; - nc._rules[nc._ruleCount].action = ZT_NETWORK_RULE_ACTION_ACCEPT; - ++nc._ruleCount; + 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"); @@ -213,14 +207,9 @@ void NetworkConfig::fromDictionary(const Dictionary &d) if (_ruleCount < ZT_MAX_NETWORK_RULES) { memset(&(_rules[_ruleCount]),0,sizeof(ZT_VirtualNetworkRule)); _rules[_ruleCount].ruleNo = rno; rno += 10; - _rules[_ruleCount].vlanId = -1; - _rules[_ruleCount].vlanPcp = -1; - _rules[_ruleCount].etherType = (et2 == 0) ? -1 : (int)et2; - _rules[_ruleCount].ipTos = -1; - _rules[_ruleCount].ipProtocol = -1; - _rules[_ruleCount].ipSourcePort = -1; - _rules[_ruleCount].ipDestPort = -1; - _rules[_ruleCount].action = ZT_NETWORK_RULE_ACTION_ACCEPT; + _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; ++_ruleCount; } } diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index c3cc9cd4..0ed7b6a2 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -133,8 +133,12 @@ public: inline bool permitsEtherType(unsigned int etherType) const { for(unsigned int i=0;i<_ruleCount;++i) { - if ((_rules[i].etherType < 0)||((unsigned int)_rules[i].etherType == etherType)) - return (_rules[i].action == ZT_NETWORK_RULE_ACTION_ACCEPT); + 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); + } } return false; } -- cgit v1.2.3