Browse Source

Add forced TCP relay mode

This patch implements a "TUNNELED" status indicator and "forceTcpRelay" setting for custom relays via local.conf.

For example:

{
  "settings":
  {
    "tcpFallbackRelay": "6.79.53.215/443",
    "forceTcpRelay":true
  }
}
Joseph Henry 2 years ago
parent
commit
eccc31a4b9
2 changed files with 42 additions and 12 deletions
  1. 17 3
      one.cpp
  2. 25 9
      service/OneService.cpp

+ 17 - 3
one.cpp

@@ -449,12 +449,16 @@ static int cli(int argc,char **argv)
 			if (json) {
 				printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
 			} else {
-				printf("200 peers\n<ztaddr>   <ver>  <role> <lat> <link> <lastTX> <lastRX> <path>" ZT_EOL_S);
+				bool anyTunneled = false;
+				printf("200 peers\n<ztaddr>   <ver>  <role> <lat> <link>   <lastTX> <lastRX> <path>" ZT_EOL_S);
 				if (j.is_array()) {
 					for(unsigned long k=0;k<j.size();++k) {
 						nlohmann::json &p = j[k];
 						std::string bestPath;
 						nlohmann::json &paths = p["paths"];
+						if (p["tunneled"]) {
+							anyTunneled = true;
+						}
 						if (paths.is_array()) {
 							for(unsigned long i=0;i<paths.size();++i) {
 								nlohmann::json &path = paths[i];
@@ -465,12 +469,19 @@ static int cli(int argc,char **argv)
 									int64_t lastSendDiff = (uint64_t)path["lastSend"] ? now - (uint64_t)path["lastSend"] : -1;
 									int64_t lastReceiveDiff = (uint64_t)path["lastReceive"] ? now - (uint64_t)path["lastReceive"] : -1;
 									OSUtils::ztsnprintf(tmp,sizeof(tmp),"%-8lld %-8lld %s",lastSendDiff,lastReceiveDiff,addr.c_str());
-									bestPath = std::string("DIRECT ") + tmp;
+									if (p["tunneled"]) {
+										bestPath = std::string("RELAY ") + tmp;
+									}
+									else {
+										bestPath = std::string("DIRECT   ") + tmp;
+									}
 									break;
 								}
 							}
 						}
-						if (bestPath.length() == 0) bestPath = "RELAY";
+						if (bestPath.length() == 0) {
+							bestPath = "RELAY";
+						}
 						char ver[128];
 						int64_t vmaj = p["versionMajor"];
 						int64_t vmin = p["versionMinor"];
@@ -489,6 +500,9 @@ static int cli(int argc,char **argv)
 							bestPath.c_str());
 					}
 				}
+				if (anyTunneled) {
+					printf("NOTE: Currently tunneling through a TCP relay. Ensure that UDP is not blocked.\n");
+				}
 			}
 			return 0;
 		} else {

+ 25 - 9
service/OneService.cpp

@@ -521,7 +521,7 @@ static void _networkToJson(nlohmann::json &nj,NetworkState &ns)
 	}
 }
 
-static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer, SharedPtr<Bond> &bond)
+static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer, SharedPtr<Bond> &bond, bool isTunneled)
 {
 	char tmp[256];
 
@@ -542,6 +542,7 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer, SharedPtr<Bond>
 	pj["latency"] = peer->latency;
 	pj["role"] = prole;
 	pj["isBonded"] = peer->isBonded;
+	pj["tunneled"] = isTunneled;
 	if (bond && peer->isBonded) {
 		pj["bondingPolicyCode"] = peer->bondingPolicy;
 		pj["bondingPolicyStr"] = Bond::getPolicyStrByCode(peer->bondingPolicy);
@@ -714,6 +715,7 @@ public:
 	PhySocket *_localControlSocket6;
 	bool _updateAutoApply;
 	bool _allowTcpFallbackRelay;
+	bool _forceTcpRelay;
 	bool _allowSecondaryPort;
 
 	unsigned int _primaryPort;
@@ -812,6 +814,7 @@ public:
 		,_localControlSocket4((PhySocket *)0)
 		,_localControlSocket6((PhySocket *)0)
 		,_updateAutoApply(false)
+		,_forceTcpRelay(false)
 		,_primaryPort(port)
 		,_udpPortPickerCounter(0)
 		,_lastDirectReceiveFromGlobal(0)
@@ -1093,7 +1096,10 @@ public:
 						if (_ports[i])
 							p[pc++] = _ports[i];
 					}
-					_binder.refresh(_phy,p,pc,explicitBind,*this);
+					if (!_forceTcpRelay) {
+						// Only bother binding UDP ports if we aren't forcing TCP-relay mode
+						_binder.refresh(_phy,p,pc,explicitBind,*this);
+					}
 					{
 						Mutex::Lock _l(_nets_m);
 						for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
@@ -1111,8 +1117,9 @@ public:
 				}
 
 				// Close TCP fallback tunnel if we have direct UDP
-				if ((_tcpFallbackTunnel)&&((now - _lastDirectReceiveFromGlobal) < (ZT_TCP_FALLBACK_AFTER / 2)))
+				if (!_forceTcpRelay && (_tcpFallbackTunnel) && ((now - _lastDirectReceiveFromGlobal) < (ZT_TCP_FALLBACK_AFTER / 2))) {
 					_phy.close(_tcpFallbackTunnel->sock);
+				}
 
 				// Sync multicast group memberships
 				if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
@@ -1488,7 +1495,7 @@ public:
 											if (ps[1] == "show") {
 												SharedPtr<Bond> bond = _node->bondController()->getBondByPeerId(wantp);
 												if (bond) {
-													_peerToJson(res,&(pl->peers[i]),bond);
+													_peerToJson(res,&(pl->peers[i]),bond,(_tcpFallbackTunnel != (TcpConnection *)0));
 													scode = 200;
 												} else {
 													scode = 400;
@@ -1530,6 +1537,7 @@ public:
 					}
 					json &settings = res["config"]["settings"];
 					settings["allowTcpFallbackRelay"] = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],_allowTcpFallbackRelay);
+					settings["forceTcpRelay"] = OSUtils::jsonBool(settings["forceTcpRelay"],_forceTcpRelay);
 					settings["primaryPort"] = OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
 					settings["secondaryPort"] = OSUtils::jsonInt(settings["secondaryPort"],(uint64_t)_secondaryPort) & 0xffff;
 					settings["tertiaryPort"] = OSUtils::jsonInt(settings["tertiaryPort"],(uint64_t)_tertiaryPort) & 0xffff;
@@ -1634,7 +1642,7 @@ public:
 									const uint64_t id = pl->peers[i].address;
 									bond = _node->bondController()->getBondByPeerId(id);
 								}
-								_peerToJson(pj,&(pl->peers[i]),bond);
+								_peerToJson(pj,&(pl->peers[i]),bond,(_tcpFallbackTunnel != (TcpConnection *)0));
 								res.push_back(pj);
 							}
 
@@ -1649,7 +1657,7 @@ public:
 									if (pl->peers[i].isBonded) {
 										bond = _node->bondController()->getBondByPeerId(wantp);
 									}
-									_peerToJson(res,&(pl->peers[i]),bond);
+									_peerToJson(res,&(pl->peers[i]),bond,(_tcpFallbackTunnel != (TcpConnection *)0));
 									scode = 200;
 									break;
 								}
@@ -2118,6 +2126,8 @@ public:
 
 		// bondingPolicy cannot be used with allowTcpFallbackRelay
 		_allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true);
+		_forceTcpRelay = OSUtils::jsonBool(settings["forceTcpRelay"],false);
+
 #ifdef ZT_TCP_FALLBACK_RELAY
 		_fallbackRelayAddress = InetAddress(OSUtils::jsonString(settings["tcpFallbackRelay"], ZT_TCP_FALLBACK_RELAY).c_str());
 #endif
@@ -2404,6 +2414,9 @@ public:
 
 	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
 	{
+		if (_forceTcpRelay) {
+			return;
+		}
 		const uint64_t now = OSUtils::now();
 		if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
 			_lastDirectReceiveFromGlobal = now;
@@ -3112,7 +3125,7 @@ public:
 					// IP address in ZT_TCP_FALLBACK_AFTER milliseconds. If we do start getting
 					// valid direct traffic we'll stop using it and close the socket after a while.
 					const int64_t now = OSUtils::now();
-					if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) {
+					if (_forceTcpRelay || (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER))) {
 						if (_tcpFallbackTunnel) {
 							bool flushNow = false;
 							{
@@ -3138,7 +3151,7 @@ public:
 								void *tmpptr = (void *)_tcpFallbackTunnel;
 								phyOnTcpWritable(_tcpFallbackTunnel->sock,&tmpptr);
 							}
-						} else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
+						} else if (_forceTcpRelay || (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2)))) {
 							const InetAddress addr(_fallbackRelayAddress);
 							TcpConnection *tc = new TcpConnection();
 							{
@@ -3159,12 +3172,15 @@ public:
 				}
 			}
 		}
+		if (_forceTcpRelay) {
+			// Shortcut here so that we don't emit any UDP packets
+			return 0;
+		}
 #endif // ZT_TCP_FALLBACK_RELAY
 
 		// Even when relaying we still send via UDP. This way if UDP starts
 		// working we can instantly "fail forward" to it and stop using TCP
 		// proxy fallback, which is slow.
-
 		if ((localSocket != -1)&&(localSocket != 0)&&(_binder.isUdpSocketValid((PhySocket *)((uintptr_t)localSocket)))) {
 			if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),ttl);
 			const bool r = _phy.udpSend((PhySocket *)((uintptr_t)localSocket),(const struct sockaddr *)addr,data,len);