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
  }
}
This commit is contained in:
Joseph Henry 2023-01-10 16:01:07 -08:00 committed by Sean OMeara
parent d31f238be0
commit eccc31a4b9
2 changed files with 42 additions and 12 deletions

16
one.cpp
View File

@ -449,12 +449,16 @@ static int cli(int argc,char **argv)
if (json) {
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
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());
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 {

View File

@ -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];
}
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);