diff options
-rw-r--r-- | attic/LockingPtr.hpp (renamed from node/LockingPtr.hpp) | 0 | ||||
-rw-r--r-- | include/ZeroTierOne.h | 85 | ||||
-rw-r--r-- | node/Capability.hpp | 33 | ||||
-rw-r--r-- | node/Hashtable.hpp | 4 | ||||
-rw-r--r-- | node/Membership.cpp | 43 | ||||
-rw-r--r-- | node/Membership.hpp | 56 | ||||
-rw-r--r-- | node/Network.cpp | 400 | ||||
-rw-r--r-- | node/Network.hpp | 7 | ||||
-rw-r--r-- | node/OutboundMulticast.cpp | 11 | ||||
-rw-r--r-- | node/Packet.hpp | 2 | ||||
-rw-r--r-- | node/Peer.cpp | 29 | ||||
-rw-r--r-- | node/Peer.hpp | 33 | ||||
-rw-r--r-- | node/Tag.hpp | 2 |
13 files changed, 561 insertions, 144 deletions
diff --git a/node/LockingPtr.hpp b/attic/LockingPtr.hpp index c373129a..c373129a 100644 --- a/node/LockingPtr.hpp +++ b/attic/LockingPtr.hpp diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 2a70417e..aa7ecc2c 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -165,9 +165,69 @@ extern "C" { #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 @@ -533,19 +593,24 @@ enum ZT_VirtualNetworkRuleType 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]; /** - * For matching tag values + * For tag-related rules */ struct { 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; } v; } ZT_VirtualNetworkRule; diff --git a/node/Capability.hpp b/node/Capability.hpp index d9b49121..53457d4d 100644 --- a/node/Capability.hpp +++ b/node/Capability.hpp @@ -52,13 +52,13 @@ class RuntimeEnvironment; * On the receiving side the receiver does the following for each packet: * * (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 - * and process this packet. + * this packet. * (3) If both check out, it receives the packet. * * 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 { @@ -248,17 +248,13 @@ public: b.append((uint16_t)rules[i].v.frameSize[0]); b.append((uint16_t)rules[i].v.frameSize[1]); 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((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; } } @@ -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 + 2); 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.value[0] = b.template at<uint32_t>(p + 4); + rules[i].v.tag.value = b.template at<uint32_t>(p + 4); break; } p += fieldLen; diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index f06b2230..c550191e 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -103,9 +103,9 @@ public: 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))), _bc(bc), _s(0) diff --git a/node/Membership.cpp b/node/Membership.cpp index a1a8eb8a..37b2c16d 100644 --- a/node/Membership.cpp +++ b/node/Membership.cpp @@ -28,49 +28,44 @@ 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 { Buffer<ZT_PROTO_MAX_PACKET_LENGTH> capsAndTags; - capsAndTags.addSize(2); 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 ((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(); capsAndTags.addSize(2); - unsigned int appendedTags = 0; 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 ((capsAndTags.size() + sizeof(Tag)) > (ZT_PROTO_MAX_PACKET_LENGTH - sizeof(CertificateOfMembership))) 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); - 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(capsAndTags.data(),capsAndTags.size()); outp.compress(); diff --git a/node/Membership.hpp b/node/Membership.hpp index 0e72b7b1..3db25488 100644 --- a/node/Membership.hpp +++ b/node/Membership.hpp @@ -40,7 +40,6 @@ namespace ZeroTier { -class Peer; class RuntimeEnvironment; /** @@ -54,18 +53,18 @@ private: struct TState { 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; // Last time we received this tag from this peer uint64_t lastReceived; - // Tag from peer + // Tag from peer (remote tag) Tag tag; }; struct CState { 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; // Last time we received this capability from this peer uint64_t lastReceived; @@ -90,29 +89,14 @@ public: * * @param RR Runtime environment * @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 * @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 @@ -132,6 +116,30 @@ public: /** * @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 id Capablity ID * @return Pointer to capability or NULL if not found */ diff --git a/node/Network.cpp b/node/Network.cpp index 485a598b..314edf5c 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -22,19 +22,313 @@ #include <math.h> #include "Constants.hpp" +#include "../version.h" #include "Network.hpp" #include "RuntimeEnvironment.hpp" +#include "MAC.hpp" +#include "Address.hpp" +#include "InetAddress.hpp" #include "Switch.hpp" -#include "Packet.hpp" #include "Buffer.hpp" +#include "Packet.hpp" #include "NetworkController.hpp" #include "Node.hpp" #include "Peer.hpp" -#include "../version.h" - 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); 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 { Mutex::Lock _l(_lock); @@ -267,6 +651,16 @@ void Network::clean() _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) diff --git a/node/Network.hpp b/node/Network.hpp index 10714a7a..a8eb3156 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -40,6 +40,7 @@ #include "MAC.hpp" #include "Dictionary.hpp" #include "Multicaster.hpp" +#include "Membership.hpp" #include "NetworkConfig.hpp" #include "CertificateOfMembership.hpp" @@ -113,7 +114,7 @@ public: * a match certain actions may be taken such as sending a copy of the packet * 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 macSource Ethernet layer source address * @param macDest Ethernet layer destination address @@ -124,7 +125,7 @@ public: * @return True if packet should be accepted locally */ bool filterIncomingPacket( - const SharedPtr<Peer> &ztSource, + const SharedPtr<Peer> &sourcePeer, const Address &ztDest, const MAC &macSource, const MAC &macDest, @@ -387,6 +388,8 @@ private: } _netconfFailure; volatile int _portError; // return value from port config callback + Hashtable<Address,Membership> _memberships; + Mutex _lock; AtomicCounter __refCount; diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index 11268fe2..a5856164 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -39,19 +39,22 @@ void OutboundMulticast::init( const void *payload, unsigned int len) { + uint8_t flags = 0; + _timestamp = timestamp; _nwid = nwid; - if (src) + if (src) { _macSrc = src; - else _macSrc.fromAddress(RR->identity.address(),nwid); + flags |= 0x04; + } else { + _macSrc.fromAddress(RR->identity.address(),nwid); + } _macDest = dest.mac(); _limit = limit; _frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU; _etherType = etherType; - uint8_t flags = 0; if (gatherLimit) flags |= 0x02; - if (src) flags |= 0x04; /* TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u", diff --git a/node/Packet.hpp b/node/Packet.hpp index 977dc1bc..6789580e 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -660,6 +660,8 @@ public: * * Flags: * 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 * superset of VERB_FRAME. They're used for bridging or when we diff --git a/node/Peer.cpp b/node/Peer.cpp index a994c4b2..ba47a0be 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -373,28 +373,15 @@ void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) 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) diff --git a/node/Peer.hpp b/node/Peer.hpp index 8b50f429..d8c44ebe 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -40,10 +40,8 @@ #include "SharedPtr.hpp" #include "AtomicCounter.hpp" #include "Hashtable.hpp" -#include "Membership.hpp" #include "Mutex.hpp" #include "NonCopyable.hpp" -#include "LockingPtr.hpp" namespace ZeroTier { @@ -387,34 +385,6 @@ public: } /** - * 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 * * @param a Peer A @@ -460,9 +430,6 @@ private: unsigned int _latency; unsigned int _directPathPushCutoffCount; - Hashtable<uint64_t,Membership> _memberships; - Mutex _memberships_m; - AtomicCounter __refCount; }; diff --git a/node/Tag.hpp b/node/Tag.hpp index dcf2eb20..a9f6f57e 100644 --- a/node/Tag.hpp +++ b/node/Tag.hpp @@ -79,7 +79,7 @@ public: inline uint64_t networkId() const { return _nwid; } inline uint64_t expiration() const { return _expiration; } 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 &signedBy() const { return _signedBy; } |