Browse Source

Refactor rules table in-memory structure in new NetworkConfig to permit far more rules with better space efficiency.

Adam Ierymenko 9 years ago
parent
commit
d736074301
4 changed files with 192 additions and 63 deletions
  1. 176 43
      include/ZeroTierOne.h
  2. 3 0
      node/Constants.hpp
  3. 7 18
      node/NetworkConfig.cpp
  4. 6 2
      node/NetworkConfig.hpp

+ 176 - 43
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;
 
 /**

+ 3 - 0
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
 

+ 7 - 18
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;
 		}
 	}

+ 6 - 2
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;
 	}