summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2014-04-10 14:22:25 -0700
committerAdam Ierymenko <adam.ierymenko@gmail.com>2014-04-10 14:22:25 -0700
commitc9294c1a78fa86fbba38b1a81988ea7527b0872c (patch)
tree0a2b3b88014b244020864839d4517a92ef1b5948
parentb117ff54358d4e2b6b8eae4bd5300464f377d948 (diff)
downloadinfinitytier-c9294c1a78fa86fbba38b1a81988ea7527b0872c.tar.gz
infinitytier-c9294c1a78fa86fbba38b1a81988ea7527b0872c.zip
Prevent recursive transit of ZeroTier packets, toward GitHub issue #56
-rw-r--r--node/AntiRecursion.hpp110
-rw-r--r--node/Constants.hpp5
-rw-r--r--node/Node.cpp16
-rw-r--r--node/Peer.cpp2
-rw-r--r--node/RuntimeEnvironment.hpp3
-rw-r--r--node/Switch.cpp18
6 files changed, 141 insertions, 13 deletions
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 <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
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 2b958088..589f8641 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -362,6 +362,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
*/
#define ZT_UPDATE_MIN_INTERVAL 120000
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 <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);
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 <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;
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> &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)