From c9294c1a78fa86fbba38b1a81988ea7527b0872c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Apr 2014 14:22:25 -0700 Subject: Prevent recursive transit of ZeroTier packets, toward GitHub issue #56 --- node/AntiRecursion.hpp | 110 ++++++++++++++++++++++++++++++++++++++++++++ node/Constants.hpp | 5 ++ node/Node.cpp | 16 ++----- node/Peer.cpp | 2 + node/RuntimeEnvironment.hpp | 3 ++ node/Switch.cpp | 18 +++++++- 6 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 node/AntiRecursion.hpp (limited to 'node') diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp new file mode 100644 index 00000000..82cf9e58 --- /dev/null +++ b/node/AntiRecursion.hpp @@ -0,0 +1,110 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_ANTIRECURSION_HPP +#define ZT_ANTIRECURSION_HPP + +#include +#include + +#include "Constants.hpp" + +namespace ZeroTier { + +#define ZT_ANTIRECURSION_TAIL_LEN 256 + +/** + * Filter to prevent recursion (ZeroTier-over-ZeroTier) + * + * This works by logging ZeroTier packets that we send. It's then invoked + * again against packets read from local Ethernet taps. If the last N + * bytes representing the ZeroTier packet match in the tap frame, then + * the frame is a re-injection of a frame that we sent and is rejected. + * + * This means that ZeroTier packets simply will not traverse ZeroTier + * networks, which would cause all sorts of weird problems. + * + * NOTE: this is applied to low-level packets before they are sent to + * SocketManager and/or sockets, not to fully assembled packets before + * (possible) fragmentation. + */ +class AntiRecursion +{ +public: + AntiRecursion() + throw() + { + memset(_history,0,sizeof(_history)); + _ptr = 0; + } + + /** + * Add an outgoing ZeroTier packet to the circular log + * + * @param data ZT packet data + * @param len Length of packet + */ + inline void logOutgoingZT(const void *data,unsigned int len) + throw() + { + ArItem *i = &(_history[_ptr++ % ZT_ANTIRECURSION_HISTORY_SIZE]); + const unsigned int tl = (len > ZT_ANTIRECURSION_TAIL_LEN) ? ZT_ANTIRECURSION_TAIL_LEN : len; + memcpy(i->tail,((const unsigned char *)data) + (len - tl),tl); + i->len = tl; + } + + /** + * Check an ethernet frame from a local tap against anti-recursion history + * + * @param data Raw frame data + * @param len Length of frame + * @return True if frame is OK to be passed, false if it's a ZT frame that we sent + */ + inline bool checkEthernetFrame(const void *data,unsigned int len) + throw() + { + for(unsigned int h=0;h= i->len)&&(!memcmp(((const unsigned char *)data) + (len - i->len),i->tail,i->len))) + return false; + } + return true; + } + +private: + struct ArItem + { + unsigned char tail[ZT_ANTIRECURSION_TAIL_LEN]; + unsigned int len; + }; + ArItem _history[ZT_ANTIRECURSION_HISTORY_SIZE]; + volatile unsigned int _ptr; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Constants.hpp b/node/Constants.hpp index 2b958088..589f8641 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -361,6 +361,11 @@ error_no_byte_order_defined; */ #define ZT_RENDEZVOUS_NAT_T_DELAY 500 +/** + * Size of anti-recursion history (see AntiRecursion.hpp) + */ +#define ZT_ANTIRECURSION_HISTORY_SIZE 16 + /** * Minimum interval between attempts to do a software update */ diff --git a/node/Node.cpp b/node/Node.cpp index 26c1d90b..655e3188 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -52,6 +52,8 @@ #include #endif +#include "../version.h" + #include "Node.hpp" #include "RuntimeEnvironment.hpp" #include "Logger.hpp" @@ -73,8 +75,7 @@ #include "SoftwareUpdater.hpp" #include "Buffer.hpp" #include "IpcConnection.hpp" - -#include "../version.h" +#include "AntiRecursion.hpp" namespace ZeroTier { @@ -235,24 +236,16 @@ struct _NodeImpl #ifndef __WINDOWS__ delete renv.netconfService; - TRACE("shutdown: delete netconfService"); #endif delete renv.updater; - TRACE("shutdown: delete updater"); delete renv.nc; - TRACE("shutdown: delete nc"); delete renv.sysEnv; - TRACE("shutdown: delete sysEnv"); delete renv.topology; - TRACE("shutdown: delete topology"); delete renv.sm; - TRACE("shutdown: delete sm"); delete renv.sw; - TRACE("shutdown: delete sw"); delete renv.mc; - TRACE("shutdown: delete mc"); + delete renv.antiRec; delete renv.prng; - TRACE("shutdown: delete prng"); delete renv.log; return reasonForTermination; @@ -477,6 +470,7 @@ Node::ReasonForTermination Node::run() Utils::lockDownFile(configAuthTokenPath.c_str(),false); // Create the objects that make up runtime state. + _r->antiRec = new AntiRecursion(); _r->mc = new Multicaster(); _r->sw = new Switch(_r); _r->sm = new SocketManager(impl->udpPort,impl->tcpPort,&_CBztTraffic,_r); diff --git a/node/Peer.cpp b/node/Peer.cpp index 5c87275f..3aeb821e 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -27,6 +27,7 @@ #include "Peer.hpp" #include "Switch.hpp" +#include "AntiRecursion.hpp" #include @@ -164,6 +165,7 @@ Path::Type Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int if ((bestPath)&&(_r->sm->send(bestPath->address(),bestPath->tcp(),bestPath->type() == Path::PATH_TYPE_TCP_OUT,data,len))) { bestPath->sent(now); + _r->antiRec->logOutgoingZT(data,len); return bestPath->type(); } return Path::PATH_TYPE_NULL; diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index e8518b97..8887b081 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -46,6 +46,7 @@ class Node; class Multicaster; class SoftwareUpdater; class SocketManager; +class AntiRecursion; /** * Holds global state for an instance of ZeroTier::Node @@ -69,6 +70,7 @@ public: timeOfLastPacketReceived(0), log((Logger *)0), prng((CMWC4096 *)0), + antiRec((AntiRecursion *)0), mc((Multicaster *)0), sw((Switch *)0), sm((SocketManager *)0), @@ -111,6 +113,7 @@ public: Logger *log; // null if logging is disabled CMWC4096 *prng; + AntiRecursion *antiRec; Multicaster *mc; Switch *sw; SocketManager *sm; diff --git a/node/Switch.cpp b/node/Switch.cpp index 1c794176..2bdc1bef 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -48,6 +48,7 @@ #include "Peer.hpp" #include "NodeConfig.hpp" #include "CMWC4096.hpp" +#include "AntiRecursion.hpp" #include "../version.h" @@ -85,6 +86,11 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (!nconf) return; + if (!_r->antiRec->checkEthernetFrame(data.data(),data.size())) { + TRACE("%s: rejected recursively addressed ZeroTier packet by tail match",network->tapDeviceName().c_str()); + return; + } + if (to == network->mac()) { LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str()); return; @@ -225,7 +231,11 @@ bool Switch::sendHELLO(const SharedPtr &dest,const Path &path) outp.append(now); _r->identity.serialize(outp,false); outp.armor(dest->key(),false); - return _r->sm->send(path.address(),path.tcp(),path.type() == Path::PATH_TYPE_TCP_OUT,outp.data(),outp.size()); + if (_r->sm->send(path.address(),path.tcp(),path.type() == Path::PATH_TYPE_TCP_OUT,outp.data(),outp.size())) { + _r->antiRec->logOutgoingZT(outp.data(),outp.size()); + return true; + } + return false; } bool Switch::sendHELLO(const SharedPtr &dest,const InetAddress &destUdp) @@ -239,7 +249,11 @@ bool Switch::sendHELLO(const SharedPtr &dest,const InetAddress &destUdp) outp.append(now); _r->identity.serialize(outp,false); outp.armor(dest->key(),false); - return _r->sm->send(destUdp,false,false,outp.data(),outp.size()); + if (_r->sm->send(destUdp,false,false,outp.data(),outp.size())) { + _r->antiRec->logOutgoingZT(outp.data(),outp.size()); + return true; + } + return false; } bool Switch::unite(const Address &p1,const Address &p2,bool force) -- cgit v1.2.3