diff --git a/node/Network.cpp b/node/Network.cpp index 1e436a27d..fc52e6d76 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -150,10 +150,8 @@ SharedPtr Network::newInstance(const RuntimeEnvironment *renv,uint64_t SharedPtr 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 wl(conf.etherTypes()); + for(std::set::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 f692c168d..47e6430c3 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 etherTypes() const + { + char tmp[16384]; + char *saveptr = (char *)0; + std::set 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::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 _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 345c79143..fd8174101 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -418,10 +418,10 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr if (network) { if (network->isAllowed(source())) { unsigned int etherType = at(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 propPeers[ZT_MULTICAST_PROPAGATION_BREADTH]; diff --git a/node/Switch.cpp b/node/Switch.cpp index 9370522e7..157ecec8d 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -85,13 +85,14 @@ void Switch::onLocalEthernet(const SharedPtr &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 79a2b04fc..8bced8ad4 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -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 *