summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
Diffstat (limited to 'service')
-rw-r--r--service/OneService.cpp201
1 files changed, 118 insertions, 83 deletions
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 593f58aa..884fc084 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -149,6 +149,7 @@ public:
bool isValidSigningIdentity(const Identity &id)
{
return (
+ /* 0001 - 0004 : obsolete, used in old versions */
/* 0005 */ (id == Identity("ba57ea350e:0:9d4be6d7f86c5660d5ee1951a3d759aa6e12a84fc0c0b74639500f1dbc1a8c566622e7d1c531967ebceb1e9d1761342f88324a8ba520c93c35f92f35080fa23f"))
/* 0006 */ ||(id == Identity("5067b21b83:0:8af477730f5055c48135b84bed6720a35bca4c0e34be4060a4c636288b1ec22217eb22709d610c66ed464c643130c51411bbb0294eef12fbe8ecc1a1e2c63a7a"))
/* 0007 */ ||(id == Identity("4f5e97a8f1:0:57880d056d7baeb04bbc057d6f16e6cb41388570e87f01492fce882485f65a798648595610a3ad49885604e7fb1db2dd3c2c534b75e42c3c0b110ad07b4bb138"))
@@ -453,6 +454,9 @@ struct TcpConnection
#define ZT_UDP_DESIRED_BUF_SIZE 131072
#endif
+// Used to pseudo-randomize local source port picking
+static volatile unsigned int _udpPortPickerCounter = 0;
+
class OneServiceImpl : public OneService
{
public:
@@ -466,14 +470,15 @@ public:
,_node((Node *)0)
,_controlPlane((ControlPlane *)0)
,_lastDirectReceiveFromGlobal(0)
- ,_lastSendToGlobal(0)
+#ifdef ZT_TCP_FALLBACK_RELAY
+ ,_lastSendToGlobalV4(0)
+#endif
,_lastRestart(0)
,_nextBackgroundTaskDeadline(0)
,_tcpFallbackTunnel((TcpConnection *)0)
,_termReason(ONE_STILL_RUNNING)
,_port(0)
#ifdef ZT_USE_MINIUPNPC
- ,_v4UpnpUdpSocket((PhySocket *)0)
,_portMapper((PortMapper *)0)
#endif
#ifdef ZT_ENABLE_CLUSTER
@@ -484,6 +489,8 @@ public:
#endif
,_run(true)
{
+ memset((void *)_udp,0,sizeof(_udp));
+
const int portTrials = (port == 0) ? 256 : 1; // if port is 0, pick random
for(int k=0;k<portTrials;++k) {
if (port == 0) {
@@ -492,20 +499,20 @@ public:
port = 40000 + (randp % 25500);
}
- _v4LocalAddress = InetAddress((uint32_t)0,port);
- _v4UdpSocket = _phy.udpBind((const struct sockaddr *)&_v4LocalAddress,reinterpret_cast<void *>(&_v4LocalAddress),ZT_UDP_DESIRED_BUF_SIZE);
+ _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 (_v4UdpSocket) {
+ 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 @localhost
+ 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);
_v4TcpListenSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
if (_v4TcpListenSocket) {
- _v6LocalAddress = InetAddress("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port);
- _v6UdpSocket = _phy.udpBind((const struct sockaddr *)&_v6LocalAddress,reinterpret_cast<void *>(&_v6LocalAddress),ZT_UDP_DESIRED_BUF_SIZE);
+ _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));
@@ -517,7 +524,7 @@ public:
_port = port;
break; // success!
} else {
- _phy.close(_v4UdpSocket,false);
+ _phy.close(_udp[0].v4s,false);
}
}
@@ -534,15 +541,18 @@ public:
virtual ~OneServiceImpl()
{
- _phy.close(_v4UdpSocket);
- _phy.close(_v6UdpSocket);
+ 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);
+ }
_phy.close(_v4TcpListenSocket);
_phy.close(_v6TcpListenSocket);
#ifdef ZT_ENABLE_CLUSTER
_phy.close(_clusterMessageSocket);
#endif
#ifdef ZT_USE_MINIUPNPC
- _phy.close(_v4UpnpUdpSocket);
delete _portMapper;
#endif
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
@@ -587,19 +597,37 @@ 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;
+ }
+ }
+ }
+
#ifdef ZT_USE_MINIUPNPC
- // Bind a secondary port for use with uPnP, since some NAT routers
- // (cough Ubiquity Edge cough) barf up a lung if you do both conventional
- // NAT-t and uPnP from behind the same port. I think this is a bug, but
- // everyone else's router bugs are our problem. :P
+ // Bind tertiary uPnP/NAT-PMP redirect port. If this succeeds start port mapper.
for(int k=0;k<512;++k) {
- unsigned int mapperPort = 40000 + (((_port + 1) * (k + 1)) % 25500);
+ const unsigned int mapperPort = 40000 + (((_port + 1) * (k + 1)) % 25500);
char uniqueName[64];
- _v4UpnpLocalAddress = InetAddress(0,mapperPort);
- _v4UpnpUdpSocket = _phy.udpBind((const struct sockaddr *)&_v4UpnpLocalAddress,reinterpret_cast<void *>(&_v4UpnpLocalAddress),ZT_UDP_DESIRED_BUF_SIZE);
- if (_v4UpnpUdpSocket) {
+ _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
+
break;
}
}
@@ -915,9 +943,10 @@ public:
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
_lastDirectReceiveFromGlobal = OSUtils::now();
- ZT_ResultCode rc = _node->processWirePacket(
+
+ const ZT_ResultCode rc = _node->processWirePacket(
OSUtils::now(),
- reinterpret_cast<const struct sockaddr_storage *>(*uptr),
+ reinterpret_cast<const struct sockaddr_storage *>(*uptr), // *uptr points to InetAddress/sockaddr of local listen port
(const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
data,
len,
@@ -1064,9 +1093,9 @@ public:
}
if (from) {
- ZT_ResultCode rc = _node->processWirePacket(
+ const ZT_ResultCode rc = _node->processWirePacket(
OSUtils::now(),
- &ZT_SOCKADDR_NULL,
+ reinterpret_cast<struct sockaddr_storage *>(&(_udp[0].v4a)), // TCP tunneled packets are "from" the default local port's address
reinterpret_cast<struct sockaddr_storage *>(&from),
data,
plen,
@@ -1281,48 +1310,30 @@ public:
inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{
-#ifdef ZT_USE_MINIUPNPC
- if ((localAddr->ss_family == AF_INET)&&(reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port == reinterpret_cast<const struct sockaddr_in *>(&_v4UpnpLocalAddress)->sin_port)) {
-#ifdef ZT_BREAK_UDP
- if (!OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) {
-#endif
- if (addr->ss_family == AF_INET) {
- if (ttl)
- _phy.setIp4UdpTtl(_v4UpnpUdpSocket,ttl);
- const int result = ((_phy.udpSend(_v4UpnpUdpSocket,(const struct sockaddr *)addr,data,len) != 0) ? 0 : -1);
- if (ttl)
- _phy.setIp4UdpTtl(_v4UpnpUdpSocket,255);
- return result;
- } else {
- return -1;
- }
-#ifdef ZT_BREAK_UDP
- }
-#endif
- }
-#endif // ZT_USE_MINIUPNPC
+ PhySocket *froms = (PhySocket *)0;
- int result = -1;
- switch(addr->ss_family) {
- case AF_INET:
-#ifdef ZT_BREAK_UDP
- if (!OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) {
-#endif
- if (_v4UdpSocket) {
- if (ttl)
- _phy.setIp4UdpTtl(_v4UdpSocket,ttl);
- result = ((_phy.udpSend(_v4UdpSocket,(const struct sockaddr *)addr,data,len) != 0) ? 0 : -1);
- if (ttl)
- _phy.setIp4UdpTtl(_v4UdpSocket,255);
+ if (addr->ss_family == AF_INET) {
+ if (reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port == 0) {
+ // If sender specifies any local address, use secondary port 1/4 times
+ froms = ((_udp[1].v4s) ? _udp[(++_udpPortPickerCounter & 0x4) >> 2].v4s : _udp[0].v4s);
+ } else {
+ // If sender specifies a local address, find it by just checking port since right now we always bind wildcard
+ for(int k=0;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 *>(addr)->sin_port) {
+ froms = _udp[k].v4s;
+ break;
}
-#ifdef ZT_BREAK_UDP
}
-#endif
+ }
+
+ if (!froms)
+ froms = _udp[0].v4s;
#ifdef ZT_TCP_FALLBACK_RELAY
- // TCP fallback tunnel support
+ // TCP fallback tunnel support, currently IPv4 only
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
- uint64_t now = OSUtils::now();
+ const uint64_t now = OSUtils::now();
// Engage TCP tunnel fallback if we haven't received anything valid from a global
// IP address in ZT_TCP_FALLBACK_AFTER milliseconds. If we do start getting
@@ -1342,8 +1353,7 @@ public:
_tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
_tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
_tcpFallbackTunnel->writeBuf.append((const char *)data,len);
- result = 0;
- } else if (((now - _lastSendToGlobal) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobal) > (ZT_PING_CHECK_INVERVAL / 2))) {
+ } else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
std::vector<InetAddress> tunnelIps(_tcpFallbackResolver.get());
if (tunnelIps.empty()) {
if (!_tcpFallbackResolver.running())
@@ -1356,27 +1366,36 @@ public:
}
}
}
-
- _lastSendToGlobal = now;
+ _lastSendToGlobalV4 = now;
}
#endif // ZT_TCP_FALLBACK_RELAY
+ } else if (addr->ss_family == AF_INET6) {
+ if (reinterpret_cast<const struct sockaddr_in6 *>(addr)->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=0;k<2;++k) {
+ // Match fast on port only, since right now we always bind wildcard
+ if (reinterpret_cast<const struct sockaddr_in6 *>(&(_udp[k].v4a))->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_port) {
+ froms = _udp[k].v4s;
+ break;
+ }
+ }
+ }
+ if (!froms)
+ froms = _udp[0].v6s;
+ } else {
+ return -1;
+ }
- break;
-
- case AF_INET6:
#ifdef ZT_BREAK_UDP
- if (!OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) {
-#endif
- if (_v6UdpSocket)
- result = ((_phy.udpSend(_v6UdpSocket,(const struct sockaddr *)addr,data,len) != 0) ? 0 : -1);
-#ifdef ZT_BREAK_UDP
- }
+ if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
+ return 0; // silently break UDP
#endif
- break;
- default:
- return -1;
- }
+ if ((ttl)&&(addr->ss_family == AF_INET))
+ _phy.setIp4UdpTtl(froms,ttl);
+ 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;
}
@@ -1485,14 +1504,32 @@ public:
#endif
Phy<OneServiceImpl *> _phy;
Node *_node;
- InetAddress _v4LocalAddress,_v6LocalAddress;
- PhySocket *_v4UdpSocket;
- PhySocket *_v6UdpSocket;
+
+ /*
+ * To properly handle NAT/gateway craziness we use three local UDP ports:
+ *
+ * [0] is the normal/default port, usually 9993
+ * [1] is a port dervied from our ZeroTier address
+ * [2] is a port computed from the normal/default for use with uPnP/NAT-PMP mappings
+ *
+ * [2] exists because on some gateways trying to do regular NAT-t interferes
+ * 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];
+
PhySocket *_v4TcpListenSocket;
PhySocket *_v6TcpListenSocket;
+
ControlPlane *_controlPlane;
+
uint64_t _lastDirectReceiveFromGlobal;
- uint64_t _lastSendToGlobal;
+#ifdef ZT_TCP_FALLBACK_RELAY
+ uint64_t _lastSendToGlobalV4;
+#endif
uint64_t _lastRestart;
volatile uint64_t _nextBackgroundTaskDeadline;
@@ -1510,8 +1547,6 @@ public:
unsigned int _port;
#ifdef ZT_USE_MINIUPNPC
- InetAddress _v4UpnpLocalAddress;
- PhySocket *_v4UpnpUdpSocket;
PortMapper *_portMapper;
#endif