Adam Ierymenko 9 年之前
父节点
当前提交
e2f783ebbd
共有 13 个文件被更改,包括 561 次插入144 次删除
  1. 0 0
      attic/LockingPtr.hpp
  2. 75 10
      include/ZeroTierOne.h
  3. 13 20
      node/Capability.hpp
  4. 2 2
      node/Hashtable.hpp
  5. 19 24
      node/Membership.cpp
  6. 32 24
      node/Membership.hpp
  7. 397 3
      node/Network.cpp
  8. 5 2
      node/Network.hpp
  9. 7 4
      node/OutboundMulticast.cpp
  10. 2 0
      node/Packet.hpp
  11. 8 21
      node/Peer.cpp
  12. 0 33
      node/Peer.hpp
  13. 1 1
      node/Tag.hpp

+ 0 - 0
node/LockingPtr.hpp → attic/LockingPtr.hpp


+ 75 - 10
include/ZeroTierOne.h

@@ -165,9 +165,69 @@ extern "C" {
 #define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
 #define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
 
 
 /**
 /**
- * Packet characteristics flag: packet direction, 1 for incoming 0 for outgoing
+ * Packet characteristics flag: packet direction, 1 if inbound 0 if outbound
  */
  */
-#define ZT_RULE_PACKET_CHARACTERISTICS_0_INBOUND 0x0000000000000001ULL
+#define ZT_RULE_PACKET_CHARACTERISTICS_INBOUND 0x8000000000000000ULL
+
+/**
+ * Packet characteristics flag: TCP left-most reserved bit
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_0 0x0000000000000800ULL
+
+/**
+ * Packet characteristics flag: TCP middle reserved bit
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_1 0x0000000000000400ULL
+
+/**
+ * Packet characteristics flag: TCP right-most reserved bit
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_2 0x0000000000000200ULL
+
+/**
+ * Packet characteristics flag: TCP NS flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_NS 0x0000000000000100ULL
+
+/**
+ * Packet characteristics flag: TCP CWR flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_CWR 0x0000000000000080ULL
+
+/**
+ * Packet characteristics flag: TCP ECE flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_ECE 0x0000000000000040ULL
+
+/**
+ * Packet characteristics flag: TCP URG flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_URG 0x0000000000000020ULL
+
+/**
+ * Packet characteristics flag: TCP ACK flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK 0x0000000000000010ULL
+
+/**
+ * Packet characteristics flag: TCP PSH flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_PSH 0x0000000000000008ULL
+
+/**
+ * Packet characteristics flag: TCP RST flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RST 0x0000000000000004ULL
+
+/**
+ * Packet characteristics flag: TCP SYN flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN 0x0000000000000002ULL
+
+/**
+ * Packet characteristics flag: TCP FIN flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 
 
 /**
 /**
  * A null/empty sockaddr (all zero) to signify an unspecified socket address
  * A null/empty sockaddr (all zero) to signify an unspecified socket address
@@ -533,19 +593,24 @@ enum ZT_VirtualNetworkRuleType
 	ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 49,
 	ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 49,
 
 
 	/**
 	/**
-	 * Match a range of tag values (equality match if start==end)
+	 * Match if local and remote tags differ by no more than value, use 0 to check for equality
+	 */
+	ZT_NETWORK_RULE_MATCH_TAGS_SAMENESS = 50,
+
+	/**
+	 * Match if local and remote tags ANDed together equal value.
 	 */
 	 */
-	ZT_NETWORK_RULE_MATCH_TAG_VALUE_RANGE = 50,
+	ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND = 51,
 
 
 	/**
 	/**
-	 * Match if all bits are set in a tag value
+	 * Match if local and remote tags ANDed together equal value.
 	 */
 	 */
-	ZT_NETWORK_RULE_MATCH_TAG_VALUE_BITS_ALL = 51,
+	ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR = 52,
 
 
 	/**
 	/**
-	 * Match if any bit from a mask is set in a tag value
+	 * Match if local and remote tags XORed together equal value.
 	 */
 	 */
-	ZT_NETWORK_RULE_MATCH_TAG_VALUE_BITS_ANY = 52
+	ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR = 53
 };
 };
 
 
 /**
 /**
@@ -650,11 +715,11 @@ typedef struct
 		uint16_t frameSize[2];
 		uint16_t frameSize[2];
 
 
 		/**
 		/**
-		 * For matching tag values
+		 * For tag-related rules
 		 */
 		 */
 		struct {
 		struct {
 			uint32_t id;
 			uint32_t id;
-			uint32_t value[2]; // only [0] is used for BITS_ALL or BITS_ANY, [0]-[1] for range
+			uint32_t value;
 		} tag;
 		} tag;
 	} v;
 	} v;
 } ZT_VirtualNetworkRule;
 } ZT_VirtualNetworkRule;

+ 13 - 20
node/Capability.hpp

@@ -52,13 +52,13 @@ class RuntimeEnvironment;
  * On the receiving side the receiver does the following for each packet:
  * On the receiving side the receiver does the following for each packet:
  *
  *
  * (1) Evaluates the capabilities of the sender (that the sender has
  * (1) Evaluates the capabilities of the sender (that the sender has
- *     presented) to determine if the sender was allowed to send this.
+ *     presented) to determine if it should received this packet.
  * (2) Evaluates its own capabilities to determine if it should receive
  * (2) Evaluates its own capabilities to determine if it should receive
- *     and process this packet.
+ *     this packet.
  * (3) If both check out, it receives the packet.
  * (3) If both check out, it receives the packet.
  *
  *
  * Note that rules in capabilities can do other things as well such as TEE
  * Note that rules in capabilities can do other things as well such as TEE
- * or REDIRECT packets. See Filter and ZT_VirtualNetworkRule.
+ * or REDIRECT packets. See filter code and ZT_VirtualNetworkRule.
  */
  */
 class Capability
 class Capability
 {
 {
@@ -248,17 +248,13 @@ public:
 					b.append((uint16_t)rules[i].v.frameSize[0]);
 					b.append((uint16_t)rules[i].v.frameSize[0]);
 					b.append((uint16_t)rules[i].v.frameSize[1]);
 					b.append((uint16_t)rules[i].v.frameSize[1]);
 					break;
 					break;
-				case ZT_NETWORK_RULE_MATCH_TAG_VALUE_RANGE:
-					b.append((uint8_t)12);
-					b.append((uint32_t)rules[i].v.tag.id);
-					b.append((uint32_t)rules[i].v.tag.value[0]);
-					b.append((uint32_t)rules[i].v.tag.value[1]);
-					break;
-				case ZT_NETWORK_RULE_MATCH_TAG_VALUE_BITS_ALL:
-				case ZT_NETWORK_RULE_MATCH_TAG_VALUE_BITS_ANY:
+				case ZT_NETWORK_RULE_MATCH_TAGS_SAMENESS:
+				case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+				case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+				case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
 					b.append((uint8_t)8);
 					b.append((uint8_t)8);
 					b.append((uint32_t)rules[i].v.tag.id);
 					b.append((uint32_t)rules[i].v.tag.id);
-					b.append((uint32_t)rules[i].v.tag.value[0]);
+					b.append((uint32_t)rules[i].v.tag.value);
 					break;
 					break;
 			}
 			}
 		}
 		}
@@ -360,15 +356,12 @@ public:
 					rules[i].v.frameSize[0] = b.template at<uint16_t>(p);
 					rules[i].v.frameSize[0] = b.template at<uint16_t>(p);
 					rules[i].v.frameSize[0] = b.template at<uint16_t>(p + 2);
 					rules[i].v.frameSize[0] = b.template at<uint16_t>(p + 2);
 					break;
 					break;
-				case ZT_NETWORK_RULE_MATCH_TAG_VALUE_RANGE:
-					rules[i].v.tag.id = b.template at<uint32_t>(p);
-					rules[i].v.tag.value[0] = b.template at<uint32_t>(p + 4);
-					rules[i].v.tag.value[1] = b.template at<uint32_t>(p + 8);
-					break;
-				case ZT_NETWORK_RULE_MATCH_TAG_VALUE_BITS_ALL:
-				case ZT_NETWORK_RULE_MATCH_TAG_VALUE_BITS_ANY:
+				case ZT_NETWORK_RULE_MATCH_TAGS_SAMENESS:
+				case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+				case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+				case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
 					rules[i].v.tag.id = b.template at<uint32_t>(p);
 					rules[i].v.tag.id = b.template at<uint32_t>(p);
-					rules[i].v.tag.value[0] = b.template at<uint32_t>(p + 4);
+					rules[i].v.tag.value = b.template at<uint32_t>(p + 4);
 					break;
 					break;
 			}
 			}
 			p += fieldLen;
 			p += fieldLen;

+ 2 - 2
node/Hashtable.hpp

@@ -103,9 +103,9 @@ public:
 	friend class Hashtable::Iterator;
 	friend class Hashtable::Iterator;
 
 
 	/**
 	/**
-	 * @param bc Initial capacity in buckets (default: 128, must be nonzero)
+	 * @param bc Initial capacity in buckets (default: 64, must be nonzero)
 	 */
 	 */
-	Hashtable(unsigned long bc = 128) :
+	Hashtable(unsigned long bc = 64) :
 		_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
 		_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
 		_bc(bc),
 		_bc(bc),
 		_s(0)
 		_s(0)

+ 19 - 24
node/Membership.cpp

@@ -28,49 +28,44 @@
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
-bool Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Peer &peer,const NetworkConfig &nconf,const uint32_t *capIds,const unsigned int capCount,const uint32_t *tagIds,const unsigned int tagCount)
+bool Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const CertificateOfMembership &com,const Capability *cap,const Tag **tags,const unsigned int tagCount)
 {
 {
 	try {
 	try {
 		Buffer<ZT_PROTO_MAX_PACKET_LENGTH> capsAndTags;
 		Buffer<ZT_PROTO_MAX_PACKET_LENGTH> capsAndTags;
 
 
-		capsAndTags.addSize(2);
 		unsigned int appendedCaps = 0;
 		unsigned int appendedCaps = 0;
-		for(unsigned int i=0;i<capCount;++i) {
-			CState *cs = _caps.get(capIds[i]);
+		if (cap) {
+			capsAndTags.addSize(2);
+			CState *const cs = _caps.get(cap->id());
 			if ((now - cs->lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) {
 			if ((now - cs->lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) {
-				if ((capsAndTags.size() + sizeof(Capability)) > (ZT_PROTO_MAX_PACKET_LENGTH - sizeof(CertificateOfMembership)))
-					break;
-				const Capability *c = nconf.capability(capIds[i]);
-				if (c) {
-					c->serialize(capsAndTags);
-					++appendedCaps;
-					cs->lastPushed = now;
-				}
+				cap->serialize(capsAndTags);
+				cs->lastPushed = now;
+				++appendedCaps;
 			}
 			}
+			capsAndTags.setAt<uint16_t>(0,(uint16_t)appendedCaps);
+		} else {
+			capsAndTags.append((uint16_t)0);
 		}
 		}
-		capsAndTags.setAt<uint16_t>(0,(uint16_t)appendedCaps);
 
 
+		unsigned int appendedTags = 0;
 		const unsigned int tagCountPos = capsAndTags.size();
 		const unsigned int tagCountPos = capsAndTags.size();
 		capsAndTags.addSize(2);
 		capsAndTags.addSize(2);
-		unsigned int appendedTags = 0;
 		for(unsigned int i=0;i<tagCount;++i) {
 		for(unsigned int i=0;i<tagCount;++i) {
-			TState *ts = _tags.get(tagIds[i]);
+			TState *const ts = _tags.get(tags[i]->id());
 			if ((now - ts->lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) {
 			if ((now - ts->lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) {
 				if ((capsAndTags.size() + sizeof(Tag)) > (ZT_PROTO_MAX_PACKET_LENGTH - sizeof(CertificateOfMembership)))
 				if ((capsAndTags.size() + sizeof(Tag)) > (ZT_PROTO_MAX_PACKET_LENGTH - sizeof(CertificateOfMembership)))
 					break;
 					break;
-				const Tag *t = nconf.tag(tagIds[i]);
-				if (t) {
-					t->serialize(capsAndTags);
-					++appendedTags;
-					ts->lastPushed = now;
-				}
+				tags[i]->serialize(capsAndTags);
+				ts->lastPushed = now;
+				++appendedTags;
 			}
 			}
 		}
 		}
 		capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags);
 		capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags);
 
 
-		if (((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY)||(appendedCaps)||(appendedTags)) {
-			Packet outp(peer.address(),RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
-			nconf.com.serialize(outp);
+		if ( ((com)&&((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY)) || (appendedCaps) || (appendedTags) ) {
+			Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+			if (com)
+				com.serialize(outp);
 			outp.append((uint8_t)0x00);
 			outp.append((uint8_t)0x00);
 			outp.append(capsAndTags.data(),capsAndTags.size());
 			outp.append(capsAndTags.data(),capsAndTags.size());
 			outp.compress();
 			outp.compress();

+ 32 - 24
node/Membership.hpp

@@ -40,7 +40,6 @@
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
-class Peer;
 class RuntimeEnvironment;
 class RuntimeEnvironment;
 
 
 /**
 /**
@@ -54,18 +53,18 @@ private:
 	struct TState
 	struct TState
 	{
 	{
 		TState() : lastPushed(0),lastReceived(0) {}
 		TState() : lastPushed(0),lastReceived(0) {}
-		// Last time we pushed this tag to this peer
+		// Last time we pushed our tag to this peer (our tag with the same ID)
 		uint64_t lastPushed;
 		uint64_t lastPushed;
 		// Last time we received this tag from this peer
 		// Last time we received this tag from this peer
 		uint64_t lastReceived;
 		uint64_t lastReceived;
-		// Tag from peer
+		// Tag from peer (remote tag)
 		Tag tag;
 		Tag tag;
 	};
 	};
 
 
 	struct CState
 	struct CState
 	{
 	{
 		CState() : lastPushed(0),lastReceived(0) {}
 		CState() : lastPushed(0),lastReceived(0) {}
-		// Last time we pushed this capability to this peer
+		// Last time we pushed our capability to this peer (our capability with this ID)
 		uint64_t lastPushed;
 		uint64_t lastPushed;
 		// Last time we received this capability from this peer
 		// Last time we received this capability from this peer
 		uint64_t lastReceived;
 		uint64_t lastReceived;
@@ -90,29 +89,14 @@ public:
 	 *
 	 *
 	 * @param RR Runtime environment
 	 * @param RR Runtime environment
 	 * @param now Current time
 	 * @param now Current time
-	 * @param peer Peer that "owns" this membership
-	 * @param nconf Network configuration
-	 * @param capIds Capability IDs that this peer might need
-	 * @param capCount Number of capability IDs
-	 * @param tagIds Tag IDs that this peer might need
+	 * @param peerAddress Address of member peer
+	 * @param com Network certificate of membership (if any)
+	 * @param cap Capability to send or 0 if none
+	 * @param tags Tags that this peer might need
 	 * @param tagCount Number of tag IDs
 	 * @param tagCount Number of tag IDs
 	 * @return True if we pushed something
 	 * @return True if we pushed something
 	 */
 	 */
-	bool sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Peer &peer,const NetworkConfig &nconf,const uint32_t *capIds,const unsigned int capCount,const uint32_t *tagIds,const unsigned int tagCount);
-
-	/**
-	 * Send COM if needed
-	 *
-	 * @param RR Runtime environment
-	 * @param now Current time
-	 * @param peer Peer that "owns" this membership
-	 * @param nconf Network configuration
-	 * @return True if we pushed something
-	 */
-	inline bool sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Peer &peer,const NetworkConfig &nconf)
-	{
-		return sendCredentialsIfNeeded(RR,now,peer,nconf,(const uint32_t *)0,0,(const uint32_t *)0,0);
-	}
+	bool sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const CertificateOfMembership &com,const Capability *cap,const Tag **tags,const unsigned int tagCount);
 
 
 	/**
 	/**
 	 * @return This peer's COM if they have sent one
 	 * @return This peer's COM if they have sent one
@@ -130,6 +114,30 @@ public:
 		return ((t) ? (((t->lastReceived != 0)&&(t->tag.expiration() < nconf.timestamp)) ? &(t->tag) : (const Tag *)0) : (const Tag *)0);
 		return ((t) ? (((t->lastReceived != 0)&&(t->tag.expiration() < nconf.timestamp)) ? &(t->tag) : (const Tag *)0) : (const Tag *)0);
 	}
 	}
 
 
+	/**
+	 * @param nconf Network configuration
+	 * @param ids Array to store IDs into
+	 * @param values Array to store values into
+	 * @param maxTags Capacity of ids[] and values[]
+	 * @return Number of tags added to arrays
+	 */
+	inline unsigned int getAllTags(const NetworkConfig &nconf,uint32_t *ids,uint32_t *values,unsigned int maxTags) const
+	{
+		unsigned int n = 0;
+		uint32_t *id = (uint32_t *)0;
+		TState *ts = (TState *)0;
+		Hashtable<uint32_t,TState>::Iterator i(const_cast<Membership *>(this)->_tags);
+		while (i.next(id,ts)) {
+			if ((ts->lastReceived)&&(ts->tag.expiration() < nconf.timestamp)) {
+				if (n >= maxTags)
+					return n;
+				ids[n] = *id;
+				values[n] = ts->tag.value();
+			}
+		}
+		return n;
+	}
+
 	/**
 	/**
 	 * @param nconf Network configuration
 	 * @param nconf Network configuration
 	 * @param id Capablity ID
 	 * @param id Capablity ID

+ 397 - 3
node/Network.cpp

@@ -22,19 +22,313 @@
 #include <math.h>
 #include <math.h>
 
 
 #include "Constants.hpp"
 #include "Constants.hpp"
+#include "../version.h"
 #include "Network.hpp"
 #include "Network.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "RuntimeEnvironment.hpp"
+#include "MAC.hpp"
+#include "Address.hpp"
+#include "InetAddress.hpp"
 #include "Switch.hpp"
 #include "Switch.hpp"
-#include "Packet.hpp"
 #include "Buffer.hpp"
 #include "Buffer.hpp"
+#include "Packet.hpp"
 #include "NetworkController.hpp"
 #include "NetworkController.hpp"
 #include "Node.hpp"
 #include "Node.hpp"
 #include "Peer.hpp"
 #include "Peer.hpp"
 
 
-#include "../version.h"
-
 namespace ZeroTier {
 namespace ZeroTier {
 
 
+// Returns true if packet appears valid; pos and proto will be set
+static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
+{
+	if (frameLen < 40)
+		return false;
+	pos = 40;
+	proto = frameData[6];
+	while (pos <= frameLen) {
+		switch(proto) {
+			case 0: // hop-by-hop options
+			case 43: // routing
+			case 60: // destination options
+			case 135: // mobility options
+				if ((pos + 8) > frameLen)
+					return false; // invalid!
+				proto = frameData[pos];
+				pos += ((unsigned int)frameData[pos + 1] * 8) + 8;
+				break;
+
+			//case 44: // fragment -- we currently can't parse these and they are deprecated in IPv6 anyway
+			//case 50:
+			//case 51: // IPSec ESP and AH -- we have to stop here since this is encrypted stuff
+			default:
+				return true;
+		}
+	}
+	return false; // overflow == invalid
+}
+
+static bool _doZtFilter(
+	const RuntimeEnvironment *RR,
+	const uint64_t nwid,
+	const bool inbound,
+	const Address &ztSource,
+	const Address &ztDest,
+	const MAC &macSource,
+	const MAC &macDest,
+	const uint8_t *frameData,
+	const unsigned int frameLen,
+	const unsigned int etherType,
+	const unsigned int vlanId,
+	const ZT_VirtualNetworkRule *rules,
+	const unsigned int ruleCount,
+	const Tag *localTags,
+	const unsigned int localTagCount,
+	const uint32_t *remoteTagIds,
+	const uint32_t *remoteTagValues,
+	const unsigned int remoteTagCount,
+	const Tag **relevantLocalTags, // pointer array must be at least [localTagCount] in size
+	unsigned int &relevantLocalTagCount)
+{
+	// For each set of rules we start by assuming that they match (since no constraints
+	// yields a 'match all' rule).
+	uint8_t thisSetMatches = 1;
+
+	for(unsigned int rn=0;rn<ruleCount;++rn) {
+		const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x7f);
+		uint8_t thisRuleMatches = 0;
+
+		switch(rt) {
+			// Actions -------------------------------------------------------------
+
+			case ZT_NETWORK_RULE_ACTION_DROP:
+				if (thisSetMatches) {
+					return false;
+				} else {
+					thisSetMatches = 1; // continue parsing next set of rules
+				}
+				break;
+			case ZT_NETWORK_RULE_ACTION_ACCEPT:
+				if (thisSetMatches) {
+					return true;
+				} else {
+					thisSetMatches = 1; // continue parsing next set of rules
+				}
+				break;
+			case ZT_NETWORK_RULE_ACTION_TEE:
+			case ZT_NETWORK_RULE_ACTION_REDIRECT: {
+				Packet outp(Address(rules[rn].v.zt),RR->identity.address(),Packet::VERB_EXT_FRAME);
+				outp.append(nwid);
+				outp.append((uint8_t)((rt == ZT_NETWORK_RULE_ACTION_REDIRECT) ? 0x04 : 0x02));
+				macDest.appendTo(outp);
+				macSource.appendTo(outp);
+				outp.append((uint16_t)etherType);
+				outp.append(frameData,frameLen);
+				outp.compress();
+				RR->sw->send(outp,true,nwid);
+
+				if (rt == ZT_NETWORK_RULE_ACTION_REDIRECT) {
+					return false;
+				} else {
+					thisSetMatches = 1; // TEE does not terminate parsing
+				}
+			}	break;
+
+			// Rules ---------------------------------------------------------------
+
+			case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+				thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
+				break;
+			case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+				thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztDest.toInt());
+				break;
+			case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+				thisRuleMatches = (uint8_t)(rules[rn].v.vlanId == (uint16_t)vlanId);
+				break;
+			case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+				// NOT SUPPORTED YET
+				thisRuleMatches = (uint8_t)(rules[rn].v.vlanPcp == 0);
+				break;
+			case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+				// NOT SUPPORTED YET
+				thisRuleMatches = (uint8_t)(rules[rn].v.vlanDei == 0);
+				break;
+			case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+				thisRuleMatches = (uint8_t)(rules[rn].v.etherType == (uint16_t)etherType);
+				break;
+			case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+				thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macSource);
+				break;
+			case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+				thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macDest);
+				break;
+			case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+					thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 12),4,0)));
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+					thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 16),4,0)));
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+				if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+					thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 8),16,0)));
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+				if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+					thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 24),16,0)));
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_IP_TOS:
+				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+					thisRuleMatches = (uint8_t)(rules[rn].v.ipTos == ((frameData[1] & 0xfc) >> 2));
+				} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+					const uint8_t trafficClass = ((frameData[0] << 4) & 0xf0) | ((frameData[1] >> 4) & 0x0f);
+					thisRuleMatches = (uint8_t)(rules[rn].v.ipTos == ((trafficClass & 0xfc) >> 2));
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+					thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == frameData[9]);
+				} else if (etherType == ZT_ETHERTYPE_IPV6) {
+					unsigned int pos = 0,proto = 0;
+					if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+						thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == (uint8_t)proto);
+					} else {
+						thisRuleMatches = 0;
+					}
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+			case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+					const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+					int p = -1;
+					switch(frameData[9]) { // IP protocol number
+						// All these start with 16-bit source and destination port in that order
+						case 0x06: // TCP
+						case 0x11: // UDP
+						case 0x84: // SCTP
+						case 0x88: // UDPLite
+							if (frameLen > (headerLen + 4)) {
+								unsigned int pos = headerLen + ((rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) ? 2 : 0);
+								p = (int)frameData[pos++] << 8;
+								p |= (int)frameData[pos];
+							}
+							break;
+					}
+					thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+				} else if (etherType == ZT_ETHERTYPE_IPV6) {
+					unsigned int pos = 0,proto = 0;
+					if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+						int p = -1;
+						switch(proto) { // IP protocol number
+							// All these start with 16-bit source and destination port in that order
+							case 0x06: // TCP
+							case 0x11: // UDP
+							case 0x84: // SCTP
+							case 0x88: // UDPLite
+								if (frameLen > (pos + 4)) {
+									if (rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) pos += 2;
+									p = (int)frameData[pos++] << 8;
+									p |= (int)frameData[pos];
+								}
+								break;
+						}
+						thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+					} else {
+						thisRuleMatches = 0;
+					}
+				} else {
+					thisRuleMatches = 0;
+				}
+				break;
+			case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: {
+				uint64_t cf = (inbound) ? ZT_RULE_PACKET_CHARACTERISTICS_INBOUND : 0ULL;
+				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)&&(frameData[9] == 0x06)) {
+					const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+					cf |= (uint64_t)frameData[headerLen + 13];
+					cf |= (((uint64_t)(frameData[headerLen + 12] & 0x0f)) << 8);
+				} else if (etherType == ZT_ETHERTYPE_IPV6) {
+					unsigned int pos = 0,proto = 0;
+					if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+						if ((proto == 0x06)&&(frameLen > (pos + 14))) {
+							cf |= (uint64_t)frameData[pos + 13];
+							cf |= (((uint64_t)(frameData[pos + 12] & 0x0f)) << 8);
+						}
+					}
+				}
+				thisRuleMatches = (uint8_t)((cf & rules[rn].v.characteristics[0]) == rules[rn].v.characteristics[1]);
+			}	break;
+			case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+				thisRuleMatches = (uint8_t)((frameLen >= (unsigned int)rules[rn].v.frameSize[0])&&(frameLen <= (unsigned int)rules[rn].v.frameSize[1]));
+				break;
+			case ZT_NETWORK_RULE_MATCH_TAGS_SAMENESS:
+			case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+			case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+			case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: {
+				const Tag *lt = (const Tag *)0;
+				for(unsigned int i=0;i<localTagCount;++i) {
+					if (rules[rn].v.tag.id == localTags[i].id()) {
+						lt = &(localTags[i]);
+						break;
+					}
+				}
+				if (!lt) {
+					thisRuleMatches = 0;
+				} else {
+					const uint32_t *rtv = (const uint32_t *)0;
+					for(unsigned int i=0;i<remoteTagCount;++i) {
+						if (rules[rn].v.tag.id == remoteTagIds[i]) {
+							rtv = &(remoteTagValues[i]);
+							break;
+						}
+					}
+					if (!rtv) {
+						thisRuleMatches = 0;
+					} else {
+						if (rt == ZT_NETWORK_RULE_MATCH_TAGS_SAMENESS) {
+							const uint32_t sameness = (lt->value() > *rtv) ? (lt->value() - *rtv) : (*rtv - lt->value());
+							thisRuleMatches = (uint8_t)(sameness <= rules[rn].v.tag.value);
+						} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND) {
+							thisRuleMatches = (uint8_t)((lt->value() & *rtv) <= rules[rn].v.tag.value);
+						} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR) {
+							thisRuleMatches = (uint8_t)((lt->value() | *rtv) <= rules[rn].v.tag.value);
+						} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR) {
+							thisRuleMatches = (uint8_t)((lt->value() ^ *rtv) <= rules[rn].v.tag.value);
+						} else { // sanity check, can't really happen
+							thisRuleMatches = 0;
+						}
+						if (thisRuleMatches) {
+							relevantLocalTags[relevantLocalTagCount++] = lt;
+						}
+					}
+				}
+			}	break;
+		}
+
+		// thisSetMatches remains true if the current rule matched... or does NOT match if not bit (0x80) is 1
+		thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t & 0x80) >> 7));
+
+		//TRACE("[%u] %u result==%u set==%u",rn,(unsigned int)rt,(unsigned int)thisRuleMatches,(unsigned int)thisSetMatches);
+	}
+
+	return false;
+}
+
 const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
 const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
 
 
 Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
 Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
@@ -100,6 +394,96 @@ Network::~Network()
 	}
 	}
 }
 }
 
 
+bool Network::filterOutgoingPacket(
+	const Address &ztSource,
+	const Address &ztDest,
+	const MAC &macSource,
+	const MAC &macDest,
+	const uint8_t *frameData,
+	const unsigned int frameLen,
+	const unsigned int etherType,
+	const unsigned int vlanId)
+{
+	uint32_t remoteTagIds[ZT_MAX_NETWORK_TAGS];
+	uint32_t remoteTagValues[ZT_MAX_NETWORK_TAGS];
+	const Tag *relevantLocalTags[ZT_MAX_NETWORK_TAGS];
+	unsigned int relevantLocalTagCount = 0;
+
+	Mutex::Lock _l(_lock);
+
+	Membership &m = _memberships[ztDest];
+	const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
+
+	if (_doZtFilter(
+		RR,
+		_id,
+		false,
+		ztSource,
+		ztDest,
+		macSource,
+		macDest,
+		frameData,
+		frameLen,
+		etherType,
+		vlanId,
+		_config.rules,
+		_config.ruleCount,
+		_config.tags,
+		_config.tagCount,
+		remoteTagIds,
+		remoteTagValues,
+		remoteTagCount,
+		relevantLocalTags,
+		relevantLocalTagCount
+	)) {
+		m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config.com,(const Capability *)0,relevantLocalTags,relevantLocalTagCount);
+		return true;
+	}
+
+	for(unsigned int c=0;c<_config.capabilityCount;++c) {
+		relevantLocalTagCount = 0;
+		if (_doZtFilter(
+			RR,
+			_id,
+			false,
+			ztSource,
+			ztDest,
+			macSource,
+			macDest,
+			frameData,
+			frameLen,
+			etherType,
+			vlanId,
+			_config.capabilities[c].rules(),
+			_config.capabilities[c].ruleCount(),
+			_config.tags,
+			_config.tagCount,
+			remoteTagIds,
+			remoteTagValues,
+			remoteTagCount,
+			relevantLocalTags,
+			relevantLocalTagCount
+		)) {
+			m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config.com,&(_config.capabilities[c]),relevantLocalTags,relevantLocalTagCount);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool Network::filterIncomingPacket(
+	const SharedPtr<Peer> &sourcePeer,
+	const Address &ztDest,
+	const MAC &macSource,
+	const MAC &macDest,
+	const uint8_t *frameData,
+	const unsigned int frameLen,
+	const unsigned int etherType,
+	const unsigned int vlanId)
+{
+}
+
 bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const
 bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const
 {
 {
 	Mutex::Lock _l(_lock);
 	Mutex::Lock _l(_lock);
@@ -267,6 +651,16 @@ void Network::clean()
 				_multicastGroupsBehindMe.erase(*mg);
 				_multicastGroupsBehindMe.erase(*mg);
 		}
 		}
 	}
 	}
+
+	{
+		Address *a = (Address *)0;
+		Membership *m = (Membership *)0;
+		Hashtable<Address,Membership>::Iterator i(_memberships);
+		while (i.next(a,m)) {
+			if ((now - m->clean(now)) > ZT_MEMBERSHIP_EXPIRATION_TIME)
+				_memberships.erase(*a);
+		}
+	}
 }
 }
 
 
 void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
 void Network::learnBridgeRoute(const MAC &mac,const Address &addr)

+ 5 - 2
node/Network.hpp

@@ -40,6 +40,7 @@
 #include "MAC.hpp"
 #include "MAC.hpp"
 #include "Dictionary.hpp"
 #include "Dictionary.hpp"
 #include "Multicaster.hpp"
 #include "Multicaster.hpp"
+#include "Membership.hpp"
 #include "NetworkConfig.hpp"
 #include "NetworkConfig.hpp"
 #include "CertificateOfMembership.hpp"
 #include "CertificateOfMembership.hpp"
 
 
@@ -113,7 +114,7 @@ public:
 	 * a match certain actions may be taken such as sending a copy of the packet
 	 * a match certain actions may be taken such as sending a copy of the packet
 	 * to a TEE or REDIRECT target.
 	 * to a TEE or REDIRECT target.
 	 *
 	 *
-	 * @param ztSource Source Peer (to save an extra lookup)
+	 * @param sourcePeer Source Peer
 	 * @param ztDest Destination ZeroTier address
 	 * @param ztDest Destination ZeroTier address
 	 * @param macSource Ethernet layer source address
 	 * @param macSource Ethernet layer source address
 	 * @param macDest Ethernet layer destination address
 	 * @param macDest Ethernet layer destination address
@@ -124,7 +125,7 @@ public:
 	 * @return True if packet should be accepted locally
 	 * @return True if packet should be accepted locally
 	 */
 	 */
 	bool filterIncomingPacket(
 	bool filterIncomingPacket(
-		const SharedPtr<Peer> &ztSource,
+		const SharedPtr<Peer> &sourcePeer,
 		const Address &ztDest,
 		const Address &ztDest,
 		const MAC &macSource,
 		const MAC &macSource,
 		const MAC &macDest,
 		const MAC &macDest,
@@ -387,6 +388,8 @@ private:
 	} _netconfFailure;
 	} _netconfFailure;
 	volatile int _portError; // return value from port config callback
 	volatile int _portError; // return value from port config callback
 
 
+	Hashtable<Address,Membership> _memberships;
+
 	Mutex _lock;
 	Mutex _lock;
 
 
 	AtomicCounter __refCount;
 	AtomicCounter __refCount;

+ 7 - 4
node/OutboundMulticast.cpp

@@ -39,19 +39,22 @@ void OutboundMulticast::init(
 	const void *payload,
 	const void *payload,
 	unsigned int len)
 	unsigned int len)
 {
 {
+	uint8_t flags = 0;
+
 	_timestamp = timestamp;
 	_timestamp = timestamp;
 	_nwid = nwid;
 	_nwid = nwid;
-	if (src)
+	if (src) {
 		_macSrc = src;
 		_macSrc = src;
-	else _macSrc.fromAddress(RR->identity.address(),nwid);
+		flags |= 0x04;
+	} else {
+		_macSrc.fromAddress(RR->identity.address(),nwid);
+	}
 	_macDest = dest.mac();
 	_macDest = dest.mac();
 	_limit = limit;
 	_limit = limit;
 	_frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
 	_frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
 	_etherType = etherType;
 	_etherType = etherType;
 
 
-	uint8_t flags = 0;
 	if (gatherLimit) flags |= 0x02;
 	if (gatherLimit) flags |= 0x02;
-	if (src) flags |= 0x04;
 
 
 	/*
 	/*
 	TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u",
 	TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u",

+ 2 - 0
node/Packet.hpp

@@ -660,6 +660,8 @@ public:
 		 *
 		 *
 		 * Flags:
 		 * Flags:
 		 *   0x01 - Certificate of network membership attached (DEPRECATED)
 		 *   0x01 - Certificate of network membership attached (DEPRECATED)
+		 *   0x02 - Packet is a TEE'd packet
+		 *   0x04 - Packet is a REDIRECT'ed packet
 		 *
 		 *
 		 * An extended frame carries full MAC addressing, making them a
 		 * An extended frame carries full MAC addressing, making them a
 		 * superset of VERB_FRAME. They're used for bridging or when we
 		 * superset of VERB_FRAME. They're used for bridging or when we

+ 8 - 21
node/Peer.cpp

@@ -373,28 +373,15 @@ void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6)
 
 
 void Peer::clean(uint64_t now)
 void Peer::clean(uint64_t now)
 {
 {
-	{
-		unsigned int np = _numPaths;
-		unsigned int x = 0;
-		unsigned int y = 0;
-		while (x < np) {
-			if (_paths[x].active(now))
-				_paths[y++] = _paths[x];
-			++x;
-		}
-		_numPaths = y;
-	}
-
-	{
-		Mutex::Lock _l(_memberships_m);
-		uint64_t *nwid = (uint64_t *)0;
-		Membership *m = (Membership *)0;
-		Hashtable<uint64_t,Membership>::Iterator i(_memberships);
-		while (i.next(nwid,m)) {
-			if ((now - m->clean(now)) > ZT_MEMBERSHIP_EXPIRATION_TIME)
-				_memberships.erase(*nwid);
-		}
+	unsigned int np = _numPaths;
+	unsigned int x = 0;
+	unsigned int y = 0;
+	while (x < np) {
+		if (_paths[x].active(now))
+			_paths[y++] = _paths[x];
+		++x;
 	}
 	}
+	_numPaths = y;
 }
 }
 
 
 void Peer::_doDeadPathDetection(Path &p,const uint64_t now)
 void Peer::_doDeadPathDetection(Path &p,const uint64_t now)

+ 0 - 33
node/Peer.hpp

@@ -40,10 +40,8 @@
 #include "SharedPtr.hpp"
 #include "SharedPtr.hpp"
 #include "AtomicCounter.hpp"
 #include "AtomicCounter.hpp"
 #include "Hashtable.hpp"
 #include "Hashtable.hpp"
-#include "Membership.hpp"
 #include "Mutex.hpp"
 #include "Mutex.hpp"
 #include "NonCopyable.hpp"
 #include "NonCopyable.hpp"
-#include "LockingPtr.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -386,34 +384,6 @@ public:
 		return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
 		return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
 	}
 	}
 
 
-	/**
-	 * Get the membership record for this network, possibly creating if missing
-	 *
-	 * @param networkId Network ID
-	 * @param createIfMissing If true, create a Membership record if there isn't one
-	 * @return Single-scope locking pointer (see LockingPtr.hpp) to Membership or NULL if not found and createIfMissing is false
-	 */
-	inline LockingPtr<Membership> membership(const uint64_t networkId,bool createIfMissing)
-	{
-		_memberships_m.lock();
-		try {
-			if (createIfMissing) {
-				return LockingPtr<Membership>(&(_memberships[networkId]),&_memberships_m);
-			} else {
-				Membership *m = _memberships.get(networkId);
-				if (m) {
-					return LockingPtr<Membership>(m,&_memberships_m);
-				} else {
-					_memberships_m.unlock();
-					return LockingPtr<Membership>();
-				}
-			}
-		} catch ( ... ) {
-			_memberships_m.unlock();
-			throw;
-		}
-	}
-
 	/**
 	/**
 	 * Find a common set of addresses by which two peers can link, if any
 	 * Find a common set of addresses by which two peers can link, if any
 	 *
 	 *
@@ -460,9 +430,6 @@ private:
 	unsigned int _latency;
 	unsigned int _latency;
 	unsigned int _directPathPushCutoffCount;
 	unsigned int _directPathPushCutoffCount;
 
 
-	Hashtable<uint64_t,Membership> _memberships;
-	Mutex _memberships_m;
-
 	AtomicCounter __refCount;
 	AtomicCounter __refCount;
 };
 };
 
 

+ 1 - 1
node/Tag.hpp

@@ -79,7 +79,7 @@ public:
 	inline uint64_t networkId() const { return _nwid; }
 	inline uint64_t networkId() const { return _nwid; }
 	inline uint64_t expiration() const { return _expiration; }
 	inline uint64_t expiration() const { return _expiration; }
 	inline uint32_t id() const { return _id; }
 	inline uint32_t id() const { return _id; }
-	inline uint32_t value() const { return _value; }
+	inline const uint32_t &value() const { return _value; }
 	inline const Address &issuedTo() const { return _issuedTo; }
 	inline const Address &issuedTo() const { return _issuedTo; }
 	inline const Address &signedBy() const { return _signedBy; }
 	inline const Address &signedBy() const { return _signedBy; }