Browse Source

OneService now binds all ports to specific local interfaces instead of wildcard and rebinds on changes. (default route)

Adam Ierymenko 9 years ago
parent
commit
32cd2a02c9
5 changed files with 296 additions and 221 deletions
  1. 83 77
      osdep/Binder.hpp
  2. 1 1
      osdep/Http.cpp
  3. 2 2
      osdep/Phy.hpp
  4. 1 1
      selftest.cpp
  5. 209 140
      service/OneService.cpp

+ 83 - 77
osdep/Binder.hpp

@@ -45,11 +45,19 @@
 #include <algorithm>
 #include <utility>
 
+#include "../node/NonCopyable.hpp"
 #include "../node/InetAddress.hpp"
 #include "../node/Mutex.hpp"
 
 #include "Phy.hpp"
 
+/**
+ * Period between binder rescans/refreshes
+ *
+ * OneService also does this on detected restarts.
+ */
+#define ZT_BINDER_REFRESH_PERIOD 30000
+
 namespace ZeroTier {
 
 /**
@@ -63,36 +71,39 @@ namespace ZeroTier {
  * On OSes that do not support local port enumeration or where this is not
  * meaningful, this degrades to binding to wildcard.
  */
-template<typename PHY_HANDLER_TYPE>
-class Binder
+class Binder : NonCopyable
 {
 private:
 	struct _Binding
 	{
+		_Binding() :
+			udpSock((PhySocket *)0),
+			tcpListenSock((PhySocket *)0),
+			address() {}
+
 		PhySocket *udpSock;
 		PhySocket *tcpListenSock;
 		InetAddress address;
 	};
 
 public:
-	/**
-	 * @param phy Physical interface to use -- be sure not to delete phy before binder
-	 * @param port Port to bind to on all interfaces (TCP and UDP)
-	 */
-	Binder(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port) :
-		_phy(phy),
-		_port(port)
-	{
-	}
+	Binder() {}
 
 	/**
-	 * Closes all bound ports -- but NOT accepted connections on those ports
+	 * Close all bound ports
+	 *
+	 * This should be called on shutdown. It closes listen sockets and UDP ports
+	 * but not TCP connections from any TCP listen sockets.
+	 *
+	 * @param phy Physical interface
 	 */
-	~Binder()
+	template<typename PHY_HANDLER_TYPE>
+	void closeAll(Phy<PHY_HANDLER_TYPE> &phy)
 	{
+		Mutex::Lock _l(_lock);
 		for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
-			_phy.close(i->udpSock,false);
-			_phy.close(i->tcpListenSock,false);
+			phy.close(i->udpSock,false);
+			phy.close(i->tcpListenSock,false);
 		}
 	}
 
@@ -102,20 +113,24 @@ public:
 	 * This should be called after wake from sleep, on detected network device
 	 * changes, on startup, or periodically (e.g. every 30-60s).
 	 *
+	 * @param phy Physical interface
+	 * @param port Port to bind to on all interfaces (TCP and UDP)
 	 * @param ignoreInterfacesByName Ignore these interfaces by name
 	 * @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*)
 	 * @param ignoreInterfacesByAddress Ignore these interfaces by address
+	 * @tparam PHY_HANDLER_TYPE Type for Phy<> template
+	 * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method
 	 */
-	void refresh(const std::vector<std::string> &ignoreInterfacesByName,const std::vector<std::string> &ignoreInterfacesByNamePrefix,const std::vector<InetAddress> &ignoreInterfacesByAddress)
+	template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
+	void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker)
 	{
-		// We use goto's in this code and some C++ compilers don't allow inline variable defs in that case, so declare them all here
 		std::vector<InetAddress> localIfAddrs;
 		std::vector<_Binding> newBindings;
 		std::vector<std::string>::const_iterator si;
 		std::vector<InetAddress>::const_iterator ii;
 		typename std::vector<_Binding>::const_iterator bi;
-		const char *na,*nb;
-		PhySocket *udps,*tcps;
+		PhySocket *udps;
+		//PhySocket *tcps;
 		InetAddress ip;
 		Mutex::Lock _l(_lock);
 
@@ -130,43 +145,21 @@ public:
 			ifa = ifatbl;
 			while (ifa) {
 				if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
-					for(si=ignoreInterfacesByName.begin();si!=ignoreInterfacesByName.end();++si) {
-						if (*si == ifa->ifa_name)
-							goto binder_hpp_ignore_interface;
-					}
-
-					for(si=ignoreInterfacesByNamePrefix.begin();si!=ignoreInterfacesByNamePrefix.end();++si) {
-						na = si->c_str();
-						nb = ifa->ifa_name;
-						while (*nb) {
-							if (*(na++) != *(nb++))
-								goto binder_hpp_interface_prefixes_dont_match;
-						}
-						goto binder_hpp_ignore_interface;
-					}
-
-binder_hpp_interface_prefixes_dont_match:
 					ip = *(ifa->ifa_addr);
-					switch(ip.ipScope()) {
-						default: break;
-						case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
-						case InetAddress::IP_SCOPE_GLOBAL:
-						//case InetAddress::IP_SCOPE_LINK_LOCAL:
-						case InetAddress::IP_SCOPE_SHARED:
-						case InetAddress::IP_SCOPE_PRIVATE:
-							for(ii=ignoreInterfacesByAddress.begin();ii!=ignoreInterfacesByAddress.end();++ii) {
-								if (ip.ipsEqual(*ii))
-									goto binder_hpp_ignore_interface;
-							}
-
-							ip.setPort(_port);
-							localIfAddrs.push_back(ip);
-
-							break;
+					if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
+						switch(ip.ipScope()) {
+							default: break;
+							case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+							case InetAddress::IP_SCOPE_GLOBAL:
+							//case InetAddress::IP_SCOPE_LINK_LOCAL:
+							case InetAddress::IP_SCOPE_SHARED:
+							case InetAddress::IP_SCOPE_PRIVATE:
+								ip.setPort(port);
+								localIfAddrs.push_back(ip);
+								break;
+						}
 					}
 				}
-
-binder_hpp_ignore_interface:
 				ifa = ifa->ifa_next;
 			}
 		}
@@ -177,15 +170,15 @@ binder_hpp_ignore_interface:
 
 		// Default to binding to wildcard if we can't enumerate addresses
 		if (localIfAddrs.size() == 0) {
-			localIfAddrs.push_back(InetAddress((uint32_t)0,_port));
-			localIfAddrs.push_back(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,_port));
+			localIfAddrs.push_back(InetAddress((uint32_t)0,port));
+			localIfAddrs.push_back(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port));
 		}
 
-		// Close any bindings to anything that doesn't exist anymore
+		// Close any old bindings to anything that doesn't exist anymore
 		for(bi=_bindings.begin();bi!=_bindings.end();++bi) {
 			if (std::find(localIfAddrs.begin(),localIfAddrs.end(),bi->address) == localIfAddrs.end()) {
-				_phy.close(bi->udpSock,false);
-				_phy.close(bi->tcpListenSock,false);
+				phy.close(bi->udpSock,false);
+				phy.close(bi->tcpListenSock,false);
 			}
 		}
 
@@ -200,21 +193,27 @@ binder_hpp_ignore_interface:
 
 			// Add new bindings
 			if (bi == _bindings.end()) {
-				udps = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
+				udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(*ii)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
 				if (udps) {
-					tcps = _phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
-					if (tcps) {
+					//tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
+					//if (tcps) {
 						newBindings.push_back(_Binding());
 						newBindings.back().udpSock = udps;
-						newBindings.back().tcpListenSock = tcps;
+						//newBindings.back().tcpListenSock = tcps;
 						newBindings.back().address = *ii;
-					} else {
-						_phy.close(udps,false);
-					}
+					//} else {
+					//	phy.close(udps,false);
+					//}
 				}
 			}
 		}
 
+		/*
+		for(bi=newBindings.begin();bi!=newBindings.end();++bi) {
+			printf("Binder: bound to %s\n",bi->address.toString().c_str());
+		}
+		*/
+
 		// Swapping pointers and then letting the old one fall out of scope is faster than copying again
 		_bindings.swap(newBindings);
 	}
@@ -242,34 +241,41 @@ binder_hpp_ignore_interface:
 	 * @param remote Remote address
 	 * @param data Data to send
 	 * @param len Length of data
+	 * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only)
 	 */
-	inline bool udpSend(const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len) const
+	template<typename PHY_HANDLER_TYPE>
+	inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
 	{
+		Mutex::Lock _l(_lock);
 		if (local) {
 			for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
-				if (i->address == local)
-					return _phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+				if (i->address == local) {
+					if ((v4ttl)&&(local.ss_family == AF_INET))
+						phy.setIp4UdpTtl(i->udpSock,v4ttl);
+					const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+					if ((v4ttl)&&(local.ss_family == AF_INET))
+						phy.setIp4UdpTtl(i->udpSock,255);
+					return result;
+				}
 			}
 			return false;
 		} else {
 			bool result = false;
 			for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
-				if (i->address.ss_family == remote.ss_family)
-					result |= _phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+				if (i->address.ss_family == remote.ss_family) {
+					if ((v4ttl)&&(remote.ss_family == AF_INET))
+						phy.setIp4UdpTtl(i->udpSock,v4ttl);
+					result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+					if ((v4ttl)&&(remote.ss_family == AF_INET))
+						phy.setIp4UdpTtl(i->udpSock,255);
+				}
 			}
 			return result;
 		}
 	}
 
-	/**
-	 * @return Local port we are handling bindings to
-	 */
-	inline unsigned int port() const { return _port; }
-
 private:
 	std::vector<_Binding> _bindings;
-	Phy<PHY_HANDLER_TYPE> &_phy;
-	unsigned int _port;
 	Mutex _lock;
 };
 

+ 1 - 1
osdep/Http.cpp

@@ -53,7 +53,7 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
 struct HttpPhyHandler
 {
 	// not used
-	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) {}
+	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) {}
 	inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {}
 
 	inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)

+ 2 - 2
osdep/Phy.hpp

@@ -89,7 +89,7 @@ typedef void PhySocket;
  *
  * For all platforms:
  *
- * phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len)
+ * phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
  * phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
  * phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
  * phyOnTcpClose(PhySocket *sock,void **uptr)
@@ -963,7 +963,7 @@ public:
 							long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen);
 							if (n > 0) {
 								try {
-									_handler->phyOnDatagram((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n);
+									_handler->phyOnDatagram((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&(s->saddr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n);
 								} catch ( ... ) {}
 							} else if (n < 0)
 								break;

+ 1 - 1
selftest.cpp

@@ -804,7 +804,7 @@ struct TestPhyHandlers;
 static Phy<TestPhyHandlers *> *testPhyInstance = (Phy<TestPhyHandlers *> *)0;
 struct TestPhyHandlers
 {
-	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len)
+	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
 	{
 		++phyTestUdpPacketCount;
 	}

+ 209 - 140
service/OneService.cpp

@@ -115,7 +115,8 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
 #define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64)
 #define ZT_MAX_HTTP_CONNECTIONS 64
 
-// Interface metric for ZeroTier taps
+// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also
+// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi.
 #define ZT_IF_METRIC 5000
 
 // How often to check for new multicast subscriptions on a tap device
@@ -473,10 +474,10 @@ public:
 	 * destructively with uPnP port mapping behavior in very weird buggy ways.
 	 * It's only used if uPnP/NAT-PMP is enabled in this build.
 	 */
-	struct {
-		InetAddress v4a,v6a;
-		PhySocket *v4s,*v6s;
-	} _udp[3];
+
+	Binder _bindings[3];
+	unsigned int _ports[3];
+	uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr
 
 	// Sockets for JSON API -- bound only to V4 and V6 localhost
 	PhySocket *_v4TcpControlSocket;
@@ -511,9 +512,6 @@ public:
 	std::string _fatalErrorMessage;
 	Mutex _termReason_m;
 
-	// The default/deterministic port we were told to use, normally 9993
-	unsigned int _port;
-
 	// uPnP/NAT-PMP port mapper if enabled
 #ifdef ZT_USE_MINIUPNPC
 	PortMapper *_portMapper;
@@ -550,7 +548,6 @@ public:
 		,_nextBackgroundTaskDeadline(0)
 		,_tcpFallbackTunnel((TcpConnection *)0)
 		,_termReason(ONE_STILL_RUNNING)
-		,_port(0)
 #ifdef ZT_USE_MINIUPNPC
 		,_portMapper((PortMapper *)0)
 #endif
@@ -562,8 +559,13 @@ public:
 #endif
 		,_run(true)
 	{
-		memset((void *)_udp,0,sizeof(_udp));
+		_ports[0] = 0;
+		_ports[1] = 0;
+		_ports[2] = 0;
 
+		// The control socket is bound to the default/static port on localhost. If we
+		// can do this, we have successfully allocated a port. The binders will take
+		// care of binding non-local addresses for ZeroTier traffic.
 		const int portTrials = (port == 0) ? 256 : 1; // if port is 0, pick random
 		for(int k=0;k<portTrials;++k) {
 			if (port == 0) {
@@ -572,59 +574,54 @@ public:
 				port = 40000 + (randp % 25500);
 			}
 
-			_udp[0].v4a = InetAddress((uint32_t)0,port);
-			_udp[0].v4s = _phy.udpBind((const struct sockaddr *)&(_udp[0].v4a),(void *)&(_udp[0].v4a),ZT_UDP_DESIRED_BUF_SIZE);
-
-			if (_udp[0].v4s) {
-				struct sockaddr_in in4;
-				memset(&in4,0,sizeof(in4));
-				in4.sin_family = AF_INET;
-				in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @127.0.0.1
-				in4.sin_port = Utils::hton((uint16_t)port);
-				_v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
-
-				if (_v4TcpControlSocket) {
-					_udp[0].v6a = InetAddress("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port);
-					_udp[0].v6s = _phy.udpBind((const struct sockaddr *)&(_udp[0].v6a),(void *)&(_udp[0].v6a),ZT_UDP_DESIRED_BUF_SIZE);
-
-					struct sockaddr_in6 in6;
-					memset((void *)&in6,0,sizeof(in6));
-					in6.sin6_family = AF_INET6;
-					in6.sin6_port = in4.sin_port;
-					in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
-					_v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
-
-					_port = port;
-					break; // success!
-				} else {
-					_phy.close(_udp[0].v4s,false);
-				}
+			struct sockaddr_in in4;
+			memset(&in4,0,sizeof(in4));
+			in4.sin_family = AF_INET;
+			in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @127.0.0.1
+			in4.sin_port = Utils::hton((uint16_t)port);
+			_v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
+
+			struct sockaddr_in6 in6;
+			memset((void *)&in6,0,sizeof(in6));
+			in6.sin6_family = AF_INET6;
+			in6.sin6_port = in4.sin_port;
+			in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
+			_v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
+
+			// We must bind one of IPv4 or IPv6 -- support either failing to support hosts that
+			// have only IPv4 or only IPv6 stacks.
+			if (((_v4TcpControlSocket)||(_v6TcpControlSocket))&&(_trialBind(port))) {
+				_ports[0] = port;
+				break;
+			} else {
+				if (_v4TcpControlSocket)
+					_phy.close(_v4TcpControlSocket,false);
+				if (_v6TcpControlSocket)
+					_phy.close(_v6TcpControlSocket,false);
+				port = 0;
 			}
-
-			port = 0;
 		}
 
-		if (_port == 0)
-			throw std::runtime_error("cannot bind to port");
+		if (_ports[0] == 0)
+			throw std::runtime_error("cannot bind to local control interface port");
 
 		char portstr[64];
-		Utils::snprintf(portstr,sizeof(portstr),"%u",_port);
+		Utils::snprintf(portstr,sizeof(portstr),"%u",_ports[0]);
 		OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr));
 	}
 
 	virtual ~OneServiceImpl()
 	{
-		for(int i=0;i<3;++i) {
-			if (_udp[i].v4s)
-				_phy.close(_udp[i].v4s);
-			if (_udp[i].v6s)
-				_phy.close(_udp[i].v6s);
-		}
+		for(int i=0;i<3;++i)
+			_bindings[i].closeAll(_phy);
+
 		_phy.close(_v4TcpControlSocket);
 		_phy.close(_v6TcpControlSocket);
+
 #ifdef ZT_ENABLE_CLUSTER
 		_phy.close(_clusterMessageSocket);
 #endif
+
 #ifdef ZT_USE_MINIUPNPC
 		delete _portMapper;
 #endif
@@ -654,7 +651,9 @@ public:
 						_termReason = ONE_UNRECOVERABLE_ERROR;
 						_fatalErrorMessage = "authtoken.secret could not be written";
 						return _termReason;
-					} else OSUtils::lockDownFile(authTokenPath.c_str(),false);
+					} else {
+						OSUtils::lockDownFile(authTokenPath.c_str(),false);
+					}
 				}
 			}
 			authToken = _trimString(authToken);
@@ -670,42 +669,47 @@ public:
 				SnodePathCheckFunction,
 				SnodeEventCallback);
 
-			// Bind secondary randomized port. If this fails we continue anyway.
-			for(int k=0;k<512;++k) {
-				const unsigned int randomizedPort = 40000 + (((unsigned int)_node->address() + k) % 25500);
-				_udp[1].v4a = InetAddress(0,randomizedPort);
-				_udp[1].v4s = _phy.udpBind((const struct sockaddr *)&(_udp[1].v4a),(void *)&(_udp[1].v4a),ZT_UDP_DESIRED_BUF_SIZE);
-				if (_udp[1].v4s) {
-					_udp[1].v6a = InetAddress("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,randomizedPort);
-					_udp[1].v6s = _phy.udpBind((const struct sockaddr *)&(_udp[1].v6a),(void *)&(_udp[1].v6a),ZT_UDP_DESIRED_BUF_SIZE);
-					if (_udp[1].v6s) {
-						break;
-					} else {
-						_phy.close(_udp[1].v4s);
-						_udp[1].v4s = (PhySocket *)0;
-					}
+			// Attempt to bind to a secondary port chosen from our ZeroTier address.
+			// This exists because there are buggy NATs out there that fail if more
+			// than one device behind the same NAT tries to use the same internal
+			// private address port number.
+			_ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
+			for(int i=0;;++i) {
+				if (i > 256) {
+					_ports[1] = 0;
+					break;
+				} else if (++_ports[1] >= 65536) {
+					_ports[1] = 20000;
 				}
+				if (_trialBind(_ports[1]))
+					break;
 			}
 
 #ifdef ZT_USE_MINIUPNPC
-			// Bind tertiary uPnP/NAT-PMP redirect port. If this succeeds start port mapper.
-			for(int k=0;k<512;++k) {
-				const unsigned int mapperPort = 40000 + (((_port + 1) * (k + 1)) % 25500);
-				char uniqueName[64];
-				_udp[2].v4a = InetAddress(0,mapperPort);
-				_udp[2].v4s = _phy.udpBind((const struct sockaddr *)&(_udp[2].v4a),(void *)&(_udp[2].v4a),ZT_UDP_DESIRED_BUF_SIZE);
-				if (_udp[2].v4s) {
-					Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx",_node->address());
-					_portMapper = new PortMapper(mapperPort,uniqueName);
-
-					_udp[2].v6a = InetAddress("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,mapperPort);
-					_udp[2].v6s = _phy.udpBind((const struct sockaddr *)&(_udp[2].v6a),(void *)&(_udp[2].v6a),ZT_UDP_DESIRED_BUF_SIZE); // okay if this fails, but it shouldn't
-
+			// If we're running uPnP/NAT-PMP, bind a *third* port for that. We can't
+			// use the other two ports for that because some NATs do really funky
+			// stuff with ports that are explicitly mapped that breaks things.
+			_ports[2] = _ports[1];
+			for(int i=0;;++i) {
+				if (i > 256) {
+					_ports[2] = 0;
 					break;
+				} else if (++_ports[2] >= 65536) {
+					_ports[2] = 20000;
 				}
+				if (_trialBind(_ports[2]))
+					break;
+			}
+			if (_ports[2]) {
+				char uniqueName[64];
+				Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx",_node->address());
+				_portMapper = new PortMapper(_ports[2],uniqueName);
 			}
 #endif
 
+			for(int i=0;i<3;++i)
+				_portsBE[i] = Utils::hton((uint16_t)_ports[i]);
+
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 			_controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
 			_node->setNetconfMaster((void *)_controller);
@@ -800,6 +804,7 @@ public:
 			_lastRestart = clockShouldBe;
 			uint64_t lastTapMulticastGroupCheck = 0;
 			uint64_t lastTcpFallbackResolve = 0;
+			uint64_t lastBindRefresh = 0;
 			uint64_t lastLocalInterfaceAddressCheck = (OSUtils::now() - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
 #ifdef ZT_AUTO_UPDATE
 			uint64_t lastSoftwareUpdateCheck = 0;
@@ -812,9 +817,28 @@ public:
 					_termReason = ONE_NORMAL_TERMINATION;
 					_termReason_m.unlock();
 					break;
-				} else _run_m.unlock();
+				} else {
+					_run_m.unlock();
+				}
+
+				const uint64_t now = OSUtils::now();
+
+				// Attempt to detect sleep/wake events by detecting delay overruns
+				bool restarted = false;
+				if ((now > clockShouldBe)&&((now - clockShouldBe) > 10000)) {
+					_lastRestart = now;
+					restarted = true;
+				}
 
-				uint64_t now = OSUtils::now();
+				// Refresh bindings in case device's interfaces have changed
+				if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
+					lastBindRefresh = now;
+					for(int i=0;i<3;++i) {
+						if (_ports[i]) {
+							_bindings[i].refresh(_phy,_ports[i],*this);
+						}
+					}
+				}
 
 				uint64_t dl = _nextBackgroundTaskDeadline;
 				if (dl <= now) {
@@ -822,10 +846,6 @@ public:
 					dl = _nextBackgroundTaskDeadline;
 				}
 
-				// Attempt to detect sleep/wake events by detecting delay overruns
-				if ((now > clockShouldBe)&&((now - clockShouldBe) > 2000))
-					_lastRestart = now;
-
 #ifdef ZT_AUTO_UPDATE
 				if ((now - lastSoftwareUpdateCheck) >= ZT_AUTO_UPDATE_CHECK_PERIOD) {
 					lastSoftwareUpdateCheck = now;
@@ -859,9 +879,11 @@ public:
 
 				_node->clearLocalInterfaceAddresses();
 #ifdef ZT_USE_MINIUPNPC
-				std::vector<InetAddress> mappedAddresses(_portMapper->get());
-				for(std::vector<InetAddress>::const_iterator ext(mappedAddresses.begin());ext!=mappedAddresses.end();++ext)
-					_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)));
+				if (_portMapper) {
+					std::vector<InetAddress> mappedAddresses(_portMapper->get());
+					for(std::vector<InetAddress>::const_iterator ext(mappedAddresses.begin());ext!=mappedAddresses.end();++ext)
+						_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)));
+				}
 #endif
 
 #ifdef __UNIX_LIKE__
@@ -885,7 +907,7 @@ public:
 								}
 								if (!isZT) {
 									InetAddress ip(ifa->ifa_addr);
-									ip.setPort(_port);
+									ip.setPort(_ports[0]);
 									_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&ip));
 								}
 							}
@@ -999,7 +1021,7 @@ public:
 
 	// Begin private implementation methods
 
-	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len)
+	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
 	{
 #ifdef ZT_ENABLE_CLUSTER
 		if (sock == _clusterMessageSocket) {
@@ -1019,7 +1041,7 @@ public:
 
 		const ZT_ResultCode rc = _node->processWirePacket(
 			OSUtils::now(),
-			reinterpret_cast<const struct sockaddr_storage *>(*uptr), // *uptr points to InetAddress/sockaddr of local listen port
+			reinterpret_cast<const struct sockaddr_storage *>(localAddr),
 			(const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
 			data,
 			len,
@@ -1072,28 +1094,32 @@ public:
 
 	inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
 	{
-		// Incoming TCP connections are HTTP JSON API requests.
-
-		TcpConnection *tc = new TcpConnection();
-		_tcpConnections.insert(tc);
-
-		tc->type = TcpConnection::TCP_HTTP_INCOMING;
-		tc->shouldKeepAlive = true;
-		tc->parent = this;
-		tc->sock = sockN;
-		tc->from = from;
-		http_parser_init(&(tc->parser),HTTP_REQUEST);
-		tc->parser.data = (void *)tc;
-		tc->messageSize = 0;
-		tc->lastActivity = OSUtils::now();
-		tc->currentHeaderField = "";
-		tc->currentHeaderValue = "";
-		tc->url = "";
-		tc->status = "";
-		tc->headers.clear();
-		tc->body = "";
-		tc->writeBuf = "";
-		*uptrN = (void *)tc;
+		if ((!from)||(reinterpret_cast<const InetAddress *>(from)->ipScope() != InetAddress::IP_SCOPE_LOOPBACK)) {
+			// Non-Loopback: deny (for now)
+			_phy.close(sockN,false);
+			return;
+		} else {
+			// Loopback == HTTP JSON API request
+			TcpConnection *tc = new TcpConnection();
+			_tcpConnections.insert(tc);
+			tc->type = TcpConnection::TCP_HTTP_INCOMING;
+			tc->shouldKeepAlive = true;
+			tc->parent = this;
+			tc->sock = sockN;
+			tc->from = from;
+			http_parser_init(&(tc->parser),HTTP_REQUEST);
+			tc->parser.data = (void *)tc;
+			tc->messageSize = 0;
+			tc->lastActivity = OSUtils::now();
+			tc->currentHeaderField = "";
+			tc->currentHeaderValue = "";
+			tc->url = "";
+			tc->status = "";
+			tc->headers.clear();
+			tc->body = "";
+			tc->writeBuf = "";
+			*uptrN = (void *)tc;
+		}
 	}
 
 	inline void phyOnTcpClose(PhySocket *sock,void **uptr)
@@ -1166,9 +1192,10 @@ public:
 							}
 
 							if (from) {
+								InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
 								const ZT_ResultCode rc = _node->processWirePacket(
 									OSUtils::now(),
-									reinterpret_cast<struct sockaddr_storage *>(&(_udp[0].v4a)), // TCP tunneled packets are "from" the default local port's address
+									reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
 									reinterpret_cast<struct sockaddr_storage *>(&from),
 									data,
 									plen,
@@ -1383,24 +1410,22 @@ public:
 
 	inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
 	{
-		PhySocket *froms = (PhySocket *)0;
+		unsigned int fromBindingNo = 0;
 
 		if (addr->ss_family == AF_INET) {
 			if (reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port == 0) {
-				// If sender specifies any local address, use secondary port 1/4 times
-				froms = _udp[(++_udpPortPickerCounter & 0x4) >> 2].v4s;
+				// If sender is sending from wildcard (null address), choose the secondary backup
+				// port 1/4 of the time. (but only for IPv4)
+				fromBindingNo = (++_udpPortPickerCounter & 0x4) >> 2;
+				if (!_ports[fromBindingNo])
+					fromBindingNo = 0;
 			} else {
-				// If sender specifies a local address, find it by just checking port since right now we always bind wildcard
-				for(int k=1;k<=2;++k) {
-					// Match fast on port only, since right now we always bind wildcard
-					if (reinterpret_cast<const struct sockaddr_in *>(&(_udp[k].v4a))->sin_port == reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port) {
-						froms = _udp[k].v4s;
-						break;
-					}
-				}
+				const uint16_t lp = reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port;
+				if (lp == _portsBE[1])
+					fromBindingNo = 1;
+				else if (lp == _portsBE[2])
+					fromBindingNo = 2;
 			}
-			if (!froms)
-				froms = _udp[0].v4s;
 
 #ifdef ZT_TCP_FALLBACK_RELAY
 			// TCP fallback tunnel support, currently IPv4 only
@@ -1442,17 +1467,12 @@ public:
 #endif // ZT_TCP_FALLBACK_RELAY
 		} else if (addr->ss_family == AF_INET6) {
 			if (reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port != 0) {
-				// If sender specifies a local address, find it by just checking port since right now we always bind wildcard
-				for(int k=1;k<=2;++k) {
-					// Match fast on port only, since right now we always bind wildcard
-					if (reinterpret_cast<const struct sockaddr_in6 *>(&(_udp[k].v6a))->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port) {
-						froms = _udp[k].v6s;
-						break;
-					}
-				}
+				const uint16_t lp = reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port;
+				if (lp == _portsBE[1])
+					fromBindingNo = 1;
+				else if (lp == _portsBE[2])
+					fromBindingNo = 2;
 			}
-			if (!froms)
-				froms = _udp[0].v6s;
 		} else {
 			return -1;
 		}
@@ -1462,12 +1482,7 @@ public:
 			return 0; // silently break UDP
 #endif
 
-		if ((ttl)&&(addr->ss_family == AF_INET))
-			_phy.setIp4UdpTtl(froms,ttl);
-		const int result = (_phy.udpSend(froms,(const struct sockaddr *)addr,data,len) != 0) ? 0 : -1;
-		if ((ttl)&&(addr->ss_family == AF_INET))
-			_phy.setIp4UdpTtl(froms,255);
-		return result;
+		return (_bindings[fromBindingNo].udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
 	}
 
 	inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
@@ -1551,6 +1566,25 @@ public:
 			_phy.close(tc->sock); // will call close handler, which deletes from _tcpConnections
 	}
 
+	bool shouldBindInterface(const char *ifname,const InetAddress &ifaddr)
+	{
+		if (isBlacklistedLocalInterfaceForZeroTierTraffic(ifname))
+			return false;
+
+		Mutex::Lock _l(_taps_m);
+		for(std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.begin());t!=_taps.end();++t) {
+			if (t->second) {
+				std::vector<InetAddress> ips(t->second->ips());
+				for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
+					if (i->ipsEqual(ifaddr))
+						return false;
+				}
+			}
+		}
+
+		return true;
+	}
+
 	std::string _dataStorePrepPath(const char *name) const
 	{
 		std::string p(_homePath);
@@ -1567,6 +1601,41 @@ public:
 		}
 		return p;
 	}
+
+	bool _trialBind(unsigned int port)
+	{
+		struct sockaddr_in in4;
+		struct sockaddr_in6 in6;
+		PhySocket *tb;
+
+		memset(&in4,0,sizeof(in4));
+		in4.sin_family = AF_INET;
+		in4.sin_port = Utils::hton((uint16_t)port);
+		tb = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&in4),(void *)0,0);
+		if (tb) {
+			_phy.close(tb,false);
+			tb = _phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&in4),(void *)0);
+			if (tb) {
+				_phy.close(tb,false);
+				return true;
+			}
+		}
+
+		memset(&in6,0,sizeof(in6));
+		in6.sin6_family = AF_INET6;
+		in6.sin6_port = Utils::hton((uint16_t)port);
+		tb = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&in6),(void *)0,0);
+		if (tb) {
+			_phy.close(tb,false);
+			tb = _phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&in6),(void *)0);
+			if (tb) {
+				_phy.close(tb,false);
+				return true;
+			}
+		}
+
+		return false;
+	}
 };
 
 static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf)