summaryrefslogtreecommitdiff
path: root/node/Network.cpp
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2016-08-05 15:02:01 -0700
committerAdam Ierymenko <adam.ierymenko@gmail.com>2016-08-05 15:02:01 -0700
commite2f783ebbd39466bc03bf115b20064d222b91944 (patch)
treee1c0bae8b5a180713435b588cecadccc858d22d5 /node/Network.cpp
parent4d9b74b171d243abe2d2d6a0039865ece8a4a00c (diff)
downloadinfinitytier-e2f783ebbd39466bc03bf115b20064d222b91944.tar.gz
infinitytier-e2f783ebbd39466bc03bf115b20064d222b91944.zip
.
Diffstat (limited to 'node/Network.cpp')
-rw-r--r--node/Network.cpp400
1 files changed, 397 insertions, 3 deletions
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)