From 55616388eaa5c43b4316882a5c4ab2e0c132b62e Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Wed, 28 Aug 2013 16:01:27 -0400
Subject: Check network ethernet type whitelist instead of hard-coded ethernet
 types.

---
 node/Network.cpp       |  9 ++++++---
 node/Network.hpp       | 47 ++++++++++++++++++++++++++++++-----------------
 node/PacketDecoder.cpp | 15 ++++++++++-----
 node/Switch.cpp        |  7 ++++---
 node/Utils.hpp         | 33 +++++++++++++++++++++++++++++++++
 5 files changed, 83 insertions(+), 28 deletions(-)

(limited to 'node')

diff --git a/node/Network.cpp b/node/Network.cpp
index 1e436a27..fc52e6d7 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -150,10 +150,8 @@ SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t
 	SharedPtr<Network> nw(new Network());
 	nw->_ready = false; // disable handling of Ethernet frames during construct
 	nw->_r = renv;
-	nw->_rlLimit.bytesPerSecond = ZT_MULTICAST_DEFAULT_BYTES_PER_SECOND;
-	nw->_rlLimit.maxBalance = ZT_MULTICAST_DEFAULT_RATE_MAX_BALANCE;
-	nw->_rlLimit.minBalance = ZT_MULTICAST_DEFAULT_RATE_MIN_BALANCE;
 	nw->_tap = new EthernetTap(renv,tag,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr());
+	memset(nw->_etWhitelist,0,sizeof(nw->_etWhitelist));
 	nw->_id = id;
 	nw->_lastConfigUpdate = 0;
 	nw->_destroyOnDelete = false;
@@ -177,6 +175,11 @@ void Network::setConfiguration(const Network::Config &conf)
 		_tap->setIps(conf.staticAddresses());
 		_tap->setDisplayName((std::string("ZeroTier One [") + conf.name() + "]").c_str());
 
+		memset(_etWhitelist,0,sizeof(_etWhitelist));
+		std::set<unsigned int> wl(conf.etherTypes());
+		for(std::set<unsigned int>::const_iterator t(wl.begin());t!=wl.end();++t)
+			_etWhitelist[*t / 8] |= (unsigned char)(1 << (*t % 8));
+
 		std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
 		if (!Utils::writeFile(confPath.c_str(),conf.toString())) {
 			LOG("error: unable to write network configuration file at: %s",confPath.c_str());
diff --git a/node/Network.hpp b/node/Network.hpp
index f692c168..47e6430c 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -267,6 +267,24 @@ public:
 			return (get("isOpen","0") == "1");
 		}
 
+		/**
+		 * @return Network ethertype whitelist
+		 */
+		inline std::set<unsigned int> etherTypes() const
+		{
+			char tmp[16384];
+			char *saveptr = (char *)0;
+			std::set<unsigned int> et;
+			if (!Utils::scopy(tmp,sizeof(tmp),get("etherTypes","").c_str()))
+				return et; // sanity check
+			for(char *f=Utils::stok(tmp,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
+				unsigned int t = Utils::stoui(f);
+				if (t)
+					et.insert(t);
+			}
+			return et;
+		}
+
 		/**
 		 * @return All static addresses / netmasks, IPv4 or IPv6
 		 */
@@ -445,22 +463,17 @@ public:
 	Status status() const;
 
 	/**
-	 * Invoke multicast rate limiter gate for a given address
-	 *
-	 * @param addr Address to check
-	 * @param bytes Bytes address wishes to send us / propagate
-	 * @return True if allowed, false if overshot rate limit
+	 * @param etherType Ethernet frame type
+	 * @return True if network permits this type
 	 */
-	inline bool multicastRateGate(const Address &addr,unsigned int bytes)
+	inline bool permitsEtherType(unsigned int etherType) const
+		throw()
 	{
-		Mutex::Lock _l(_lock);
-		std::map<Address,RateLimiter>::iterator rl(_multicastRateLimiters.find(addr));
-		if (rl == _multicastRateLimiters.end()) {
-			RateLimiter &newrl = _multicastRateLimiters[addr];
-			newrl.init(ZT_MULTICAST_DEFAULT_RATE_PRELOAD);
-			return newrl.gate(_rlLimit,(double)bytes);
-		}
-		return rl->second.gate(_rlLimit,(double)bytes);
+		if (!etherType)
+			return false;
+		else if (etherType > 65535)
+			return false;
+		else return ((_etWhitelist[etherType / 8] & (unsigned char)(1 << (etherType % 8))) != 0);
 	}
 
 private:
@@ -469,9 +482,6 @@ private:
 
 	const RuntimeEnvironment *_r;
 
-	// Rate limits for this network
-	RateLimiter::Limit _rlLimit;
-
 	// Tap and tap multicast memberships
 	EthernetTap *_tap;
 	std::set<MulticastGroup> _multicastGroups;
@@ -486,6 +496,9 @@ private:
 	Config _configuration;
 	Certificate _myCertificate;
 
+	// Ethertype whitelist bit field, set from config, for really fast lookup
+	unsigned char _etWhitelist[65536 / 8];
+
 	uint64_t _id;
 	volatile uint64_t _lastConfigUpdate;
 	volatile bool _destroyOnDelete;
diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp
index 345c7914..fd817410 100644
--- a/node/PacketDecoder.cpp
+++ b/node/PacketDecoder.cpp
@@ -418,10 +418,10 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 		if (network) {
 			if (network->isAllowed(source())) {
 				unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
-				if ((etherType != ZT_ETHERTYPE_ARP)&&(etherType != ZT_ETHERTYPE_IPV4)&&(etherType != ZT_ETHERTYPE_IPV6)) {
-					TRACE("dropped FRAME from %s: unsupported ethertype",source().toString().c_str());
-				} else if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
+				if (network->permitsEtherType(etherType)) {
 					network->tap().put(source().toMAC(),network->tap().mac(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD);
+				} else if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
+					TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id());
 				}
 			} else {
 				TRACE("dropped FRAME from %s(%s): not a member of closed network %llu",source().toString().c_str(),_remoteAddress.toString().c_str(),network->id());
@@ -509,8 +509,8 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 				return true;
 			}
 
-			if (++hops >= ZT_MULTICAST_PROPAGATION_DEPTH) {
-				TRACE("dropped MULTICAST_FRAME from original submitter %s, received from %s(%s): max depth reached",originalSubmitterAddress.toString().c_str(),source().toString().c_str(),_remoteAddress.toString().c_str());
+			if (!network->permitsEtherType(etherType)) {
+				LOG("dropped MULTICAST_FRAME from original submitter %s, received from %s(%s): ethernet type %s not allowed on network %.16llx",originalSubmitterAddress.toString().c_str(),source().toString().c_str(),_remoteAddress.toString().c_str(),Filter::etherTypeName(etherType),(unsigned long long)network->id());
 				return true;
 			}
 
@@ -533,6 +533,11 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 				_r->multicaster->addToDedupHistory(mccrc,now);
 			}
 
+			if (++hops >= ZT_MULTICAST_PROPAGATION_DEPTH) {
+				TRACE("not propagating MULTICAST_FRAME from original submitter %s, received from %s(%s): max depth reached",originalSubmitterAddress.toString().c_str(),source().toString().c_str(),_remoteAddress.toString().c_str());
+				return true;
+			}
+
 			Address upstream(source()); // save this since we might mangle it below
 			Multicaster::MulticastBloomFilter bloom(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_BLOOM_FILTER,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BYTES));
 			SharedPtr<Peer> propPeers[ZT_MULTICAST_PROPAGATION_BREADTH];
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 9370522e..157ecec8 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -85,13 +85,14 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		LOG("ignored tap: %s -> %s %s (bridging is not (yet?) supported)",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType));
 		return;
 	}
+
 	if (to == network->tap().mac()) {
-		LOG("%s: frame received from self, ignoring (bridge loop?)",network->tap().deviceName().c_str());
+		LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tap().deviceName().c_str());
 		return;
 	}
 
-	if ((etherType != ZT_ETHERTYPE_ARP)&&(etherType != ZT_ETHERTYPE_IPV4)&&(etherType != ZT_ETHERTYPE_IPV6)) {
-		LOG("ignored tap: %s -> %s %s (not a supported etherType)",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType));
+	if (!network->permitsEtherType(etherType)) {
+		LOG("ignored tap: %s -> %s: ethernet type %s not allowed on network %.16llx",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType),(unsigned long long)network->id());
 		return;
 	}
 
diff --git a/node/Utils.hpp b/node/Utils.hpp
index 79a2b04f..8bced8ad 100644
--- a/node/Utils.hpp
+++ b/node/Utils.hpp
@@ -31,6 +31,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <string.h>
 #include <time.h>
 
 #include <string>
@@ -460,6 +461,38 @@ public:
 #endif
 	}
 
+	// String to int converters (and hex string to int)
+	static inline unsigned int stoui(const char *s)
+		throw()
+	{
+		return (unsigned int)strtoul(s,(char **)0,10);
+	}
+	static inline unsigned long stoul(const char *s)
+		throw()
+	{
+		return strtoul(s,(char **)0,10);
+	}
+	static inline unsigned long long stoull(const char *s)
+		throw()
+	{
+		return strtoull(s,(char **)0,10);
+	}
+	static inline unsigned int hstoui(const char *s)
+		throw()
+	{
+		return (unsigned int)strtoul(s,(char **)0,16);
+	}
+	static inline unsigned long hstoul(const char *s)
+		throw()
+	{
+		return strtoul(s,(char **)0,16);
+	}
+	static inline unsigned long long hstoull(const char *s)
+		throw()
+	{
+		return strtoull(s,(char **)0,16);
+	}
+
 	/**
 	 * Perform a safe C string copy
 	 *
-- 
cgit v1.2.3