Browse Source

Prevent recursive transit of ZeroTier packets, toward GitHub issue #56

Adam Ierymenko 11 years ago
parent
commit
c9294c1a78
6 changed files with 141 additions and 13 deletions
  1. 110 0
      node/AntiRecursion.hpp
  2. 5 0
      node/Constants.hpp
  3. 5 11
      node/Node.cpp
  4. 2 0
      node/Peer.cpp
  5. 3 0
      node/RuntimeEnvironment.hpp
  6. 16 2
      node/Switch.cpp

+ 110 - 0
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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+
+#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<ZT_ANTIRECURSION_HISTORY_SIZE;++h) {
+			ArItem *i = &(_history[h]);
+			if ((len >= 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

+ 5 - 0
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
  */

+ 5 - 11
node/Node.cpp

@@ -52,6 +52,8 @@
 #include <sys/file.h>
 #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);

+ 2 - 0
node/Peer.cpp

@@ -27,6 +27,7 @@
 
 #include "Peer.hpp"
 #include "Switch.hpp"
+#include "AntiRecursion.hpp"
 
 #include <algorithm>
 
@@ -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;

+ 3 - 0
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;

+ 16 - 2
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> &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<Peer> &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<Peer> &dest,const InetAddress &destUdp)
@@ -239,7 +249,11 @@ bool Switch::sendHELLO(const SharedPtr<Peer> &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)