From 3c9ea2b6674819612cd88a2e141e0f8f633b06cd Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 5 Dec 2022 13:21:05 -0800 Subject: [PATCH 01/17] Add low-bandwidth mode --- node/Multicaster.cpp | 3 ++- node/Node.cpp | 20 ++++++++++++++++---- node/Node.hpp | 11 +++++++++++ node/Peer.cpp | 15 ++++++++++----- node/Peer.hpp | 2 ++ service/OneService.cpp | 1 + 6 files changed, 42 insertions(+), 10 deletions(-) diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 4856b88ee..d93b2ae10 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -268,7 +268,8 @@ void Multicaster::send( const unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1; - if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) { + int timerScale = RR->node->lowBandwidthModeEnabled() ? 3 : 1; + if ((gs.members.empty())||((now - gs.lastExplicitGather) >= (ZT_MULTICAST_EXPLICIT_GATHER_DELAY * timerScale))) { gs.lastExplicitGather = now; Address explicitGatherPeers[16]; diff --git a/node/Node.cpp b/node/Node.cpp index 019a8afca..7bdf331dc 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -202,6 +202,14 @@ public: { const std::vector *const alwaysContactEndpoints = _alwaysContact.get(p->address()); if (alwaysContactEndpoints) { + + // Contact upstream peers as infrequently as possible + ZT_PeerRole role = RR->topology->role(p->address()); + int roleBasedTimerScale = (role == ZT_PEER_ROLE_LEAF) ? 2 : 16; + if ((RR->node->now() - p->lastSentFullHello()) <= (ZT_PATH_HEARTBEAT_PERIOD * roleBasedTimerScale)) { + return; + } + const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now); bool contacted = (sent != 0); @@ -262,7 +270,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 } } - unsigned long timeUntilNextPingCheck = ZT_PING_CHECK_INVERVAL; + unsigned long timeUntilNextPingCheck = _lowBandwidthMode ? (ZT_PING_CHECK_INVERVAL * 5) : ZT_PING_CHECK_INVERVAL; const int64_t timeSinceLastPingCheck = now - _lastPingCheck; if (timeSinceLastPingCheck >= timeUntilNextPingCheck) { try { @@ -309,6 +317,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 // Get peers we should stay connected to according to network configs // Also get networks and whether they need config so we only have to do one pass over networks + int timerScale = _lowBandwidthMode ? 64 : 1; std::vector< std::pair< SharedPtr,bool > > networkConfigNeeded; { Mutex::Lock l(_networks_m); @@ -317,7 +326,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 SharedPtr *network = (SharedPtr *)0; while (i.next(nwid,network)) { (*network)->config().alwaysContactAddresses(alwaysContact); - networkConfigNeeded.push_back( std::pair< SharedPtr,bool >(*network,(((now - (*network)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*network)->hasConfig()))) ); + networkConfigNeeded.push_back( std::pair< SharedPtr,bool >(*network,(((now - (*network)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY * timerScale)||(!(*network)->hasConfig()))) ); } } @@ -336,9 +345,12 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 // Refresh network config or broadcast network updates to members as needed for(std::vector< std::pair< SharedPtr,bool > >::const_iterator n(networkConfigNeeded.begin());n!=networkConfigNeeded.end();++n) { - if (n->second) + if (n->second) { n->first->requestConfiguration(tptr); - n->first->sendUpdatesToMembers(tptr); + } + if (! _lowBandwidthMode) { + n->first->sendUpdatesToMembers(tptr); + } } // Update online status, post status change as event diff --git a/node/Node.hpp b/node/Node.hpp index 834f50cc9..0ddd5cb7b 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -269,6 +269,16 @@ public: _stats.inVerbBytes[v] += (uint64_t)bytes; } + inline void setLowBandwidthMode(bool isEnabled) + { + _lowBandwidthMode = isEnabled; + } + + inline bool lowBandwidthModeEnabled() + { + return _lowBandwidthMode; + } + private: RuntimeEnvironment _RR; RuntimeEnvironment *RR; @@ -316,6 +326,7 @@ private: int64_t _lastMemoizedTraceSettings; volatile int64_t _prngState[2]; bool _online; + bool _lowBandwidthMode; }; } // namespace ZeroTier diff --git a/node/Peer.cpp b/node/Peer.cpp index 99fa8d277..c1dc124c6 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -219,11 +219,15 @@ void Peer::received( // is done less frequently. if (this->trustEstablished(now)) { const int64_t sinceLastPush = now - _lastDirectPathPushSent; - if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) { + bool lowBandwidth = RR->node->lowBandwidthModeEnabled(); + int timerScale = lowBandwidth ? 16 : 1; + if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH * timerScale : ZT_DIRECT_PATH_PUSH_INTERVAL)) { _lastDirectPathPushSent = now; std::vector pathsToPush(RR->node->directPaths()); - std::vector ma = RR->sa->whoami(); - pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end()); + if (! lowBandwidth) { + std::vector ma = RR->sa->whoami(); + pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end()); + } if (!pathsToPush.empty()) { std::vector::const_iterator p(pathsToPush.begin()); while (p != pathsToPush.end()) { @@ -453,7 +457,7 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA if (atAddress) { outp.armor(_key,false,nullptr); // false == don't encrypt full payload, but add MAC RR->node->expectReplyTo(outp.packetId()); - RR->node->putPacket(tPtr,-1,atAddress,outp.data(),outp.size()); + RR->node->putPacket(tPtr,RR->node->lowBandwidthModeEnabled() ? localSocket : -1,atAddress,outp.data(),outp.size()); } else { RR->node->expectReplyTo(outp.packetId()); RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC @@ -477,8 +481,9 @@ void Peer::tryMemorizedPath(void *tPtr,int64_t now) if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) { _lastTriedMemorizedPath = now; InetAddress mp; - if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp)) + if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp)) { attemptToContactAt(tPtr,-1,mp,now,true); + } } } diff --git a/node/Peer.hpp b/node/Peer.hpp index 0192143e3..668bbbee9 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -302,6 +302,8 @@ public: */ inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); } + inline int64_t lastSentFullHello() { return _lastSentFullHello; } + /** * @return Latency in milliseconds of best/aggregate path or 0xffff if unknown / no paths */ diff --git a/service/OneService.cpp b/service/OneService.cpp index daba4d99b..21717e3a5 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -2122,6 +2122,7 @@ public: fprintf(stderr,"WARNING: using manually-specified secondary and/or tertiary ports. This can cause NAT issues." ZT_EOL_S); } _portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true); + _node->setLowBandwidthMode(OSUtils::jsonBool(settings["lowBandwidthMode"],false)); #ifndef ZT_SDK const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT)); From db1df58955c655e166b214ec49038efd925d6b21 Mon Sep 17 00:00:00 2001 From: travis laduke Date: Mon, 5 Dec 2022 15:33:01 -0800 Subject: [PATCH 02/17] resurrect tcp proxy --- .gitignore | 3 +- tcp-proxy/Makefile | 7 + tcp-proxy/README.md | 4 + tcp-proxy/tcp-proxy.cpp | 317 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 tcp-proxy/Makefile create mode 100644 tcp-proxy/README.md create mode 100644 tcp-proxy/tcp-proxy.cpp diff --git a/.gitignore b/.gitignore index 198a7f669..79a9e247c 100755 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,5 @@ zeroidc/target/ __pycache__ *.pyc *_source.tar.bz2 -snap/.snapcraft \ No newline at end of file +snap/.snapcraft +tcp-proxy/tcp-proxy diff --git a/tcp-proxy/Makefile b/tcp-proxy/Makefile new file mode 100644 index 000000000..af4e71e3a --- /dev/null +++ b/tcp-proxy/Makefile @@ -0,0 +1,7 @@ +CXX=$(shell which clang++ g++ c++ 2>/dev/null | head -n 1) + +all: + $(CXX) -O3 -fno-rtti -o tcp-proxy tcp-proxy.cpp + +clean: + rm -f *.o tcp-proxy *.dSYM diff --git a/tcp-proxy/README.md b/tcp-proxy/README.md new file mode 100644 index 000000000..5af078a20 --- /dev/null +++ b/tcp-proxy/README.md @@ -0,0 +1,4 @@ +TCP Proxy Server +====== + +This is the TCP proxy server we run for TCP tunneling from peers behind difficult NATs. Regular users won't have much use for this. diff --git a/tcp-proxy/tcp-proxy.cpp b/tcp-proxy/tcp-proxy.cpp new file mode 100644 index 000000000..d57351987 --- /dev/null +++ b/tcp-proxy/tcp-proxy.cpp @@ -0,0 +1,317 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + */ + +// HACK! Will eventually use epoll() or something in Phy<> instead of select(). +// Also be sure to change ulimit -n and fs.file-max in /etc/sysctl.conf on relays. +#if defined(__linux__) || defined(__LINUX__) || defined(__LINUX) || defined(LINUX) +#include +#include +#undef __FD_SETSIZE +#define __FD_SETSIZE 1048576 +#undef FD_SETSIZE +#define FD_SETSIZE 1048576 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../osdep/Phy.hpp" + +#define ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS 300 +#define ZT_TCP_PROXY_TCP_PORT 443 + +using namespace ZeroTier; + +/* + * ZeroTier TCP Proxy Server + * + * This implements a simple packet encapsulation that is designed to look like + * a TLS connection. It's not a TLS connection, but it sends TLS format record + * headers. It could be extended in the future to implement a fake TLS + * handshake. + * + * At the moment, each packet is just made to look like TLS application data: + * <[1] TLS content type> - currently 0x17 for "application data" + * <[1] TLS major version> - currently 0x03 for TLS 1.2 + * <[1] TLS minor version> - currently 0x03 for TLS 1.2 + * <[2] payload length> - 16-bit length of payload in bytes + * <[...] payload> - Message payload + * + * TCP is inherently inefficient for encapsulating Ethernet, since TCP and TCP + * like protocols over TCP lead to double-ACKs. So this transport is only used + * to enable access when UDP or other datagram protocols are not available. + * + * Clients send a greeting, which is a four-byte message that contains: + * <[1] ZeroTier major version> + * <[1] minor version> + * <[2] revision> + * + * If a client has sent a greeting, it uses the new version of this protocol + * in which every encapsulated ZT packet is prepended by an IP address where + * it should be forwarded (or where it came from for replies). This causes + * this proxy to act as a remote UDP socket similar to a socks proxy, which + * will allow us to move this function off the rootservers and onto dedicated + * proxy nodes. + * + * Older ZT clients that do not send this message get their packets relayed + * to/from 127.0.0.1:9993, which will allow them to talk to and relay via + * the ZT node on the same machine as the proxy. We'll only support this for + * as long as such nodes appear to be in the wild. + */ + +struct TcpProxyService; +struct TcpProxyService +{ + Phy *phy; + int udpPortCounter; + struct Client + { + char tcpReadBuf[131072]; + char tcpWriteBuf[131072]; + unsigned long tcpWritePtr; + unsigned long tcpReadPtr; + PhySocket *tcp; + PhySocket *udp; + time_t lastActivity; + bool newVersion; + }; + std::map< PhySocket *,Client > clients; + + PhySocket *getUnusedUdp(void *uptr) + { + for(int i=0;i<65535;++i) { + ++udpPortCounter; + if (udpPortCounter > 0xfffe) + udpPortCounter = 1024; + struct sockaddr_in laddr; + memset(&laddr,0,sizeof(struct sockaddr_in)); + laddr.sin_family = AF_INET; + laddr.sin_port = htons((uint16_t)udpPortCounter); + PhySocket *udp = phy->udpBind(reinterpret_cast(&laddr),uptr); + if (udp) + return udp; + } + return (PhySocket *)0; + } + + void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) + { + if (!*uptr) + return; + if ((from->sa_family == AF_INET)&&(len >= 16)&&(len < 2048)) { + Client &c = *((Client *)*uptr); + c.lastActivity = time((time_t *)0); + + unsigned long mlen = len; + if (c.newVersion) + mlen += 7; // new clients get IP info + + if ((c.tcpWritePtr + 5 + mlen) <= sizeof(c.tcpWriteBuf)) { + if (!c.tcpWritePtr) + phy->setNotifyWritable(c.tcp,true); + + c.tcpWriteBuf[c.tcpWritePtr++] = 0x17; // look like TLS data + c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2 + c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2 + + c.tcpWriteBuf[c.tcpWritePtr++] = (char)((mlen >> 8) & 0xff); + c.tcpWriteBuf[c.tcpWritePtr++] = (char)(mlen & 0xff); + + if (c.newVersion) { + c.tcpWriteBuf[c.tcpWritePtr++] = (char)4; // IPv4 + *((uint32_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_addr.s_addr; + c.tcpWritePtr += 4; + *((uint16_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_port; + c.tcpWritePtr += 2; + } + + for(unsigned long i=0;i %.16llx\n",inet_ntoa(reinterpret_cast(from)->sin_addr),(int)ntohs(reinterpret_cast(from)->sin_port),(unsigned long long)&c); + } + } + + void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) + { + // unused, we don't initiate outbound connections + } + + void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) + { + Client &c = clients[sockN]; + PhySocket *udp = getUnusedUdp((void *)&c); + if (!udp) { + phy->close(sockN); + clients.erase(sockN); + printf("** TCP rejected, no more UDP ports to assign\n"); + return; + } + c.tcpWritePtr = 0; + c.tcpReadPtr = 0; + c.tcp = sockN; + c.udp = udp; + c.lastActivity = time((time_t *)0); + c.newVersion = false; + *uptrN = (void *)&c; + printf("<< TCP from %s -> %.16llx\n",inet_ntoa(reinterpret_cast(from)->sin_addr),(unsigned long long)&c); + } + + void phyOnTcpClose(PhySocket *sock,void **uptr) + { + if (!*uptr) + return; + Client &c = *((Client *)*uptr); + phy->close(c.udp); + clients.erase(sock); + printf("** TCP %.16llx closed\n",(unsigned long long)*uptr); + } + + void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) + { + Client &c = *((Client *)*uptr); + c.lastActivity = time((time_t *)0); + + for(unsigned long i=0;i= sizeof(c.tcpReadBuf)) { + phy->close(sock); + return; + } + c.tcpReadBuf[c.tcpReadPtr++] = ((const char *)data)[i]; + + if (c.tcpReadPtr >= 5) { + unsigned long mlen = ( ((((unsigned long)c.tcpReadBuf[3]) & 0xff) << 8) | (((unsigned long)c.tcpReadBuf[4]) & 0xff) ); + if (c.tcpReadPtr >= (mlen + 5)) { + if (mlen == 4) { + // Right now just sending this means the client is 'new enough' for the IP header + c.newVersion = true; + printf("<< TCP %.16llx HELLO\n",(unsigned long long)*uptr); + } else if (mlen >= 7) { + char *payload = c.tcpReadBuf + 5; + unsigned long payloadLen = mlen; + + struct sockaddr_in dest; + memset(&dest,0,sizeof(dest)); + if (c.newVersion) { + if (*payload == (char)4) { + // New clients tell us where their packets go. + ++payload; + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = *((uint32_t *)payload); + payload += 4; + dest.sin_port = *((uint16_t *)payload); // will be in network byte order already + payload += 2; + payloadLen -= 7; + } + } else { + // For old clients we will just proxy everything to a local ZT instance. The + // fact that this will come from 127.0.0.1 will in turn prevent that instance + // from doing unite() with us. It'll just forward. There will not be many of + // these. + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1 + dest.sin_port = htons(9993); + } + + // Note: we do not relay to privileged ports... just an abuse prevention rule. + if ((ntohs(dest.sin_port) > 1024)&&(payloadLen >= 16)) { + phy->udpSend(c.udp,(const struct sockaddr *)&dest,payload,payloadLen); + printf(">> TCP %.16llx to %s:%d\n",(unsigned long long)*uptr,inet_ntoa(dest.sin_addr),(int)ntohs(dest.sin_port)); + } + } + + memmove(c.tcpReadBuf,c.tcpReadBuf + (mlen + 5),c.tcpReadPtr -= (mlen + 5)); + } + } + } + } + + void phyOnTcpWritable(PhySocket *sock,void **uptr) + { + Client &c = *((Client *)*uptr); + if (c.tcpWritePtr) { + long n = phy->streamSend(sock,c.tcpWriteBuf,c.tcpWritePtr); + if (n > 0) { + memmove(c.tcpWriteBuf,c.tcpWriteBuf + n,c.tcpWritePtr -= (unsigned long)n); + if (!c.tcpWritePtr) + phy->setNotifyWritable(sock,false); + } + } else phy->setNotifyWritable(sock,false); + } + + void doHousekeeping() + { + std::vector toClose; + time_t now = time((time_t *)0); + for(std::map< PhySocket *,Client >::iterator c(clients.begin());c!=clients.end();++c) { + if ((now - c->second.lastActivity) >= ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS) { + toClose.push_back(c->first); + toClose.push_back(c->second.udp); + } + } + for(std::vector::iterator s(toClose.begin());s!=toClose.end();++s) + phy->close(*s); + } +}; + +int main(int argc,char **argv) +{ + signal(SIGPIPE,SIG_IGN); + signal(SIGHUP,SIG_IGN); + srand(time((time_t *)0)); + + TcpProxyService svc; + Phy phy(&svc,false,true); + svc.phy = &phy; + svc.udpPortCounter = 1023; + + { + struct sockaddr_in laddr; + memset(&laddr,0,sizeof(laddr)); + laddr.sin_family = AF_INET; + laddr.sin_port = htons(ZT_TCP_PROXY_TCP_PORT); + if (!phy.tcpListen((const struct sockaddr *)&laddr)) { + fprintf(stderr,"%s: fatal error: unable to bind TCP port %d\n",argv[0],ZT_TCP_PROXY_TCP_PORT); + return 1; + } + } + + time_t lastDidHousekeeping = time((time_t *)0); + for(;;) { + phy.poll(120000); + time_t now = time((time_t *)0); + if ((now - lastDidHousekeeping) > 120) { + lastDidHousekeeping = now; + svc.doHousekeeping(); + } + } + + return 0; +} From b8eb9196e891ed4e4dc2670e11f14e86fb237a99 Mon Sep 17 00:00:00 2001 From: travis laduke Date: Mon, 5 Dec 2022 15:38:10 -0800 Subject: [PATCH 03/17] update tcp-relay README --- tcp-proxy/README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tcp-proxy/README.md b/tcp-proxy/README.md index 5af078a20..0eccf23fe 100644 --- a/tcp-proxy/README.md +++ b/tcp-proxy/README.md @@ -2,3 +2,34 @@ TCP Proxy Server ====== This is the TCP proxy server we run for TCP tunneling from peers behind difficult NATs. Regular users won't have much use for this. + +## How to run your own +Currently you must build it and distribute it to your server manually. + +To reduce latency, the tcp-relay should be as close as possible to the nodes it is serving. A datacenter in the same city or the LAN would be ideal. + + +### Build +`cd tcp-relay` +`make` + +### Point your node at it + The default tcp relay is at `204.80.128.1/443` -an anycast address. + +#### Option 1 - local.conf configuration +See [Service docs](https://github.com/zerotier/ZeroTierOne/blob/e0acccc3c918b59678033e585b31eb000c68fdf2/service/README.md) for more info on local.conf +`{ "settings": { "tcpFallbackRelay": "198.51.100.123/443" } }` + + +#### Option 2 - redirect 204.80.128.1 to your own IP + +If you are the admin of the network that is blocking ZeroTier UDP, you can transparently redirect 204.80.128.1 to one of your IP addresses. Users won't need to edit their local client configuration. + +Configuring this in your Enterprise Firewall is left as an exercise to the reader. + +Here is an iptables example for illustrative purposes: + +``` shell +-A PREROUTING -p tcp -d 204.80.128.1 --dport 443 -j DNAT --to-destination 198.51.100.123 +-A POSTROUTING -p tcp -d 198.51.100.123 --dport 443 -j SNAT --to-source 204.80.128.1 +``` From 1e2ff042b487adf41c6ff3eab3d3bac5cc809cdd Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Wed, 7 Dec 2022 10:17:53 -0500 Subject: [PATCH 04/17] fix typos in docs, comments, and strings --- controller/PostgreSQL.cpp | 8 ++++---- controller/README.md | 2 +- doc/zerotier-one.8.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 63d32b637..397c1f8f2 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -113,7 +113,7 @@ MemberNotificationReceiver::MemberNotificationReceiver(PostgreSQL *p, pqxx::conn : pqxx::notification_receiver(c, channel) , _psql(p) { - fprintf(stderr, "initialize MemberNotificaitonReceiver\n"); + fprintf(stderr, "initialize MemberNotificationReceiver\n"); } @@ -140,7 +140,7 @@ NetworkNotificationReceiver::NetworkNotificationReceiver(PostgreSQL *p, pqxx::co } void NetworkNotificationReceiver::operator() (const std::string &payload, int packend_pid) { - fprintf(stderr, "Network Notificaiton received: %s\n", payload.c_str()); + fprintf(stderr, "Network Notification received: %s\n", payload.c_str()); json tmp(json::parse(payload)); json &ov = tmp["old_val"]; json &nv = tmp["new_val"]; @@ -185,7 +185,7 @@ PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, R fprintf(stderr, "ZT_SSO_PSK: %s\n", ssoPskHex); #endif if (ssoPskHex) { - // SECURITY: note that ssoPskHex will always be null-terminated if libc acatually + // SECURITY: note that ssoPskHex will always be null-terminated if libc actually // returns something non-NULL. If the hex encodes something shorter than 48 bytes, // it will be padded at the end with zeroes. If longer, it'll be truncated. Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk)); @@ -1586,7 +1586,7 @@ void PostgreSQL::onlineNotificationThread() /** * ONLY UNCOMMENT FOR TEMPORARY DB MAINTENANCE * - * This define temproarly turns off writing to the member status table + * This define temporarily turns off writing to the member status table * so it can be reindexed when the indexes get too large. */ diff --git a/controller/README.md b/controller/README.md index 0a76ebbc7..331710a15 100644 --- a/controller/README.md +++ b/controller/README.md @@ -246,7 +246,7 @@ This returns a JSON object containing all member IDs as keys and their `memberRe | vMajor | integer | Most recently known major version | no | | vMinor | integer | Most recently known minor version | no | | vRev | integer | Most recently known revision | no | -| vProto | integer | Most recently known protocl version | no | +| vProto | integer | Most recently known protocol version | no | Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored. diff --git a/doc/zerotier-one.8.md b/doc/zerotier-one.8.md index bd31d5c80..4f4655076 100644 --- a/doc/zerotier-one.8.md +++ b/doc/zerotier-one.8.md @@ -81,7 +81,7 @@ These are found in the service's working directory. If the ZeroTier One service is built with the network controller enabled, it periodically backs up its controller.db database in this file (currently every 5 minutes if there have been changes). Since this file is not a currently in use SQLite3 database it's safer to back up without corruption. On new backups the file is rotated out rather than being rewritten in place. * `iddb.d/` (directory): - Caches the public identity of every peer ZeroTier has spoken with in the last 60 days. This directory and its contents can be deleted, but this may result in slower connection initations since it will require that we go out and re-fetch full identities for peers we're speaking to. + Caches the public identity of every peer ZeroTier has spoken with in the last 60 days. This directory and its contents can be deleted, but this may result in slower connection initiations since it will require that we go out and re-fetch full identities for peers we're speaking to. * `networks.d` (directory): This caches network configurations and certificate information for networks you belong to. ZeroTier scans this directory for .conf files on startup to recall its networks, so "touch"ing an empty .conf file in this directory is a way of pre-configuring ZeroTier to join a specific network on startup without using the API. If the config file is empty ZeroTIer will just fetch it from the network's controller. From 98e0bf22d3214c000f3c56acee901d6f7056c7bf Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Wed, 7 Dec 2022 10:22:54 -0500 Subject: [PATCH 05/17] fix actual typo in config key code --- controller/PostgreSQL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index 397c1f8f2..ef1ba0ff9 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -1387,7 +1387,7 @@ void PostgreSQL::commitThread() "sso_enabled = EXCLUDED.sso_enabled", id, _myAddressStr, - OSUtils::jsonDump(config["capabilitles"], -1), + OSUtils::jsonDump(config["capabilities"], -1), (bool)config["enableBroadcast"], OSUtils::now(), (int)config["mtu"], From 7587ef51362e2e4fab774965d0513ca95a7d26ba Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Mon, 12 Dec 2022 09:44:31 +0100 Subject: [PATCH 06/17] basic builds on github (#1815) Initial Github Actions build --- .github/workflows/build.yml | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..06f416606 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +on: [ push, pull_request ] + +jobs: + build_ubuntu: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: make + run: make + + - name: selftest + run: make selftest + + # build_macos: + # runs-on: macos-latest + # steps: + # - name: checkout + # uses: actions/checkout@v3 + + # - name: make + # run: make + + # - name: selftest + # run: make selftest + + # build_windows: + # runs-on: windows-latest + # steps: + # - name: checkout + # uses: actions/checkout@v3 + + # - name: setup msbuild + # uses: microsoft/setup-msbuild@v1.1.3 + + # - name: msbuild + # run: | + # msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild + From 117d7194af58ccf64f7cc11ffcb11a730ebece93 Mon Sep 17 00:00:00 2001 From: Travis LaDuke Date: Tue, 13 Dec 2022 02:52:21 -0800 Subject: [PATCH 07/17] Improve default route on macOS (#1680) re: issue #1088 and probably: https://discuss.zerotier.com/t/default-route-issue-osx-monterey-m1/6974 // current zerotier. // allow default adds two sets of routes. ``` netstat -rnfinet | grep "/1\|default" | sort 0/1 10.2.0.2 UGScg feth4823 0/1 192.168.82.1 UGScIg en7 128.0/1 10.2.0.2 UGSc feth4823 128.0/1 192.168.82.1 UGScI en7 default 192.168.82.1 UGScg en7 ``` Then, something chaotic happens eventually, and networking stops working. // after patch ``` netstat -rnfinet | grep "/1\|default" | sort 0/1 10.2.0.2 UGScg feth4823 128.0/1 10.2.0.2 UGSc feth4823 default 192.168.82.1 UGScg en7 ``` After the change, I can still: - use default route - route to other subnets I tested on high sierra through monterey and on freebsd13.1 --- osdep/ManagedRoute.cpp | 70 ------------------------------------------ 1 file changed, 70 deletions(-) diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp index 325f4c803..64342e2c2 100644 --- a/osdep/ManagedRoute.cpp +++ b/osdep/ManagedRoute.cpp @@ -439,76 +439,6 @@ bool ManagedRoute::sync() #ifdef __BSD__ // ------------------------------------------------------------ - if (_device[0]) { - bool haveDevice = false; - struct ifaddrs *ifa = (struct ifaddrs *)0; - if (!getifaddrs(&ifa)) { - struct ifaddrs *p = ifa; - while (p) { - if ((p->ifa_name)&&(!strcmp(_device, p->ifa_name))) { - haveDevice = true; - break; - } - p = p->ifa_next; - } - freeifaddrs(ifa); - } - if (!haveDevice) - return false; - } - - // Find lowest metric system route that this route should override (if any) - InetAddress newSystemVia; - char newSystemDevice[128]; - newSystemDevice[0] = (char)0; - int systemMetric = 9999999; - std::vector<_RTE> rtes(_getRTEs(_target,false)); - for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) { - if (r->via) { - if ( ((!newSystemVia)||(r->metric < systemMetric)) && (strcmp(r->device,_device) != 0) ) { - newSystemVia = r->via; - Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device); - systemMetric = r->metric; - } - } - } - - // Get device corresponding to route if we don't have that already - if ((newSystemVia)&&(!newSystemDevice[0])) { - rtes = _getRTEs(newSystemVia,true); - for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) { - if ( (r->device[0]) && (strcmp(r->device,_device) != 0) ) { - Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device); - break; - } - } - } - if (!newSystemDevice[0]) - newSystemVia.zero(); - - // Shadow system route if it exists, also delete any obsolete shadows - // and replace them with the new state. sync() is called periodically to - // allow us to do that if underlying connectivity changes. - if ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice) != 0)) { - if (_systemVia) { - _routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0); - if (rightt) - _routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0); - } - - _systemVia = newSystemVia; - Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice); - - if (_systemVia) { - _routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0); - //_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0); - if (rightt) { - _routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0); - //_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0); - } - } - } - //if (!_applied.count(leftt)) { _applied[leftt] = !_via; //_routeCmd("delete",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device); From 0210ba9c13bae2c46408129ae6e56934fe17d63c Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Tue, 13 Dec 2022 16:16:17 +0100 Subject: [PATCH 08/17] enabling mac and windows github builds --- .github/workflows/build.yml | 39 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06f416606..54aa2a058 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,28 +13,27 @@ jobs: - name: selftest run: make selftest - # build_macos: - # runs-on: macos-latest - # steps: - # - name: checkout - # uses: actions/checkout@v3 + build_macos: + runs-on: macos-latest + steps: + - name: checkout + uses: actions/checkout@v3 - # - name: make - # run: make + - name: make + run: make - # - name: selftest - # run: make selftest + - name: selftest + run: make selftest - # build_windows: - # runs-on: windows-latest - # steps: - # - name: checkout - # uses: actions/checkout@v3 + build_windows: + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v3 - # - name: setup msbuild - # uses: microsoft/setup-msbuild@v1.1.3 + - name: setup msbuild + uses: microsoft/setup-msbuild@v1.1.3 - # - name: msbuild - # run: | - # msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild - + - name: msbuild + run: | + msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild From d2de0292ccd4b22227b653624de0bb893d850dfe Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Tue, 13 Dec 2022 16:49:08 +0100 Subject: [PATCH 09/17] mac and windows github builds (#1817) --- .github/workflows/build.yml | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54aa2a058..d60c4709d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,36 +1,44 @@ -on: [ push, pull_request ] - +on: [ push ] + jobs: build_ubuntu: runs-on: ubuntu-latest steps: - - name: checkout - uses: actions/checkout@v3 - - - name: make - run: make - - - name: selftest - run: make selftest + - name: checkout + uses: actions/checkout@v3 + + - name: make + run: make + + - name: selftest + run: make selftest build_macos: runs-on: macos-latest steps: - name: checkout uses: actions/checkout@v3 - + + - name: Install Rust Toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: aarch64-apple-darwin + override: true + components: rustfmt, clippy + - name: make run: make - + - name: selftest run: make selftest - + build_windows: runs-on: windows-latest steps: - name: checkout uses: actions/checkout@v3 - + - name: setup msbuild uses: microsoft/setup-msbuild@v1.1.3 From e0e91e83976d91fc5c3ad60136bf5fad420fdeff Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Tue, 13 Dec 2022 17:20:52 +0100 Subject: [PATCH 10/17] disabling windows builds --- .github/workflows/build.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d60c4709d..db836c6ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,15 +33,15 @@ jobs: - name: selftest run: make selftest - build_windows: - runs-on: windows-latest - steps: - - name: checkout - uses: actions/checkout@v3 + # build_windows: + # runs-on: windows-latest + # steps: + # - name: checkout + # uses: actions/checkout@v3 - - name: setup msbuild - uses: microsoft/setup-msbuild@v1.1.3 + # - name: setup msbuild + # uses: microsoft/setup-msbuild@v1.1.3 - - name: msbuild - run: | - msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild + # - name: msbuild + # run: | + # msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild From 10170b41c30d4bffde3a691562cf41c8afc823cd Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Tue, 13 Dec 2022 18:32:07 +0100 Subject: [PATCH 11/17] Revert "Improve default route on macOS (#1680)" This reverts commit 117d7194af58ccf64f7cc11ffcb11a730ebece93. --- osdep/ManagedRoute.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp index 64342e2c2..325f4c803 100644 --- a/osdep/ManagedRoute.cpp +++ b/osdep/ManagedRoute.cpp @@ -439,6 +439,76 @@ bool ManagedRoute::sync() #ifdef __BSD__ // ------------------------------------------------------------ + if (_device[0]) { + bool haveDevice = false; + struct ifaddrs *ifa = (struct ifaddrs *)0; + if (!getifaddrs(&ifa)) { + struct ifaddrs *p = ifa; + while (p) { + if ((p->ifa_name)&&(!strcmp(_device, p->ifa_name))) { + haveDevice = true; + break; + } + p = p->ifa_next; + } + freeifaddrs(ifa); + } + if (!haveDevice) + return false; + } + + // Find lowest metric system route that this route should override (if any) + InetAddress newSystemVia; + char newSystemDevice[128]; + newSystemDevice[0] = (char)0; + int systemMetric = 9999999; + std::vector<_RTE> rtes(_getRTEs(_target,false)); + for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) { + if (r->via) { + if ( ((!newSystemVia)||(r->metric < systemMetric)) && (strcmp(r->device,_device) != 0) ) { + newSystemVia = r->via; + Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device); + systemMetric = r->metric; + } + } + } + + // Get device corresponding to route if we don't have that already + if ((newSystemVia)&&(!newSystemDevice[0])) { + rtes = _getRTEs(newSystemVia,true); + for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) { + if ( (r->device[0]) && (strcmp(r->device,_device) != 0) ) { + Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device); + break; + } + } + } + if (!newSystemDevice[0]) + newSystemVia.zero(); + + // Shadow system route if it exists, also delete any obsolete shadows + // and replace them with the new state. sync() is called periodically to + // allow us to do that if underlying connectivity changes. + if ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice) != 0)) { + if (_systemVia) { + _routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0); + if (rightt) + _routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0); + } + + _systemVia = newSystemVia; + Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice); + + if (_systemVia) { + _routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0); + //_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0); + if (rightt) { + _routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0); + //_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0); + } + } + } + //if (!_applied.count(leftt)) { _applied[leftt] = !_via; //_routeCmd("delete",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device); From 3e41163bbea2a7ff79213c51462b7bf4b2adb4f6 Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Wed, 14 Dec 2022 11:52:30 +0100 Subject: [PATCH 12/17] fixing windows github builds (#1818) --- .github/workflows/build.yml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db836c6ca..8d9dc461d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,6 @@ jobs: toolchain: stable target: aarch64-apple-darwin override: true - components: rustfmt, clippy - name: make run: make @@ -33,15 +32,20 @@ jobs: - name: selftest run: make selftest - # build_windows: - # runs-on: windows-latest - # steps: - # - name: checkout - # uses: actions/checkout@v3 + build_windows: + runs-on: windows-latest + steps: + - name: gitconfig + run: | + git config --global core.autocrlf false + git config --global core.eol lf - # - name: setup msbuild - # uses: microsoft/setup-msbuild@v1.1.3 + - name: checkout + uses: actions/checkout@v3 - # - name: msbuild - # run: | - # msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild + - name: setup msbuild + uses: microsoft/setup-msbuild@v1.1.3 + + - name: msbuild + run: | + msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild From a558bd93121fc7be31e67a683aa75b44ebb5e16f Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Wed, 14 Dec 2022 19:10:08 +0100 Subject: [PATCH 13/17] cache cago on github actions (#1819) --- .github/workflows/build.yml | 97 +++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d9dc461d..1f76d5b67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,33 +4,73 @@ jobs: build_ubuntu: runs-on: ubuntu-latest steps: - - name: checkout - uses: actions/checkout@v3 - - - name: make - run: make - - - name: selftest - run: make selftest - - build_macos: - runs-on: macos-latest - steps: + - name: gitconfig + run: | + git config --global core.autocrlf false + git config --global core.eol lf - name: checkout uses: actions/checkout@v3 - - - name: Install Rust Toolchain + - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable target: aarch64-apple-darwin override: true + components: rustfmt, clippy + - name: Set up cargo cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- - name: make run: make - - name: selftest - run: make selftest + run: | + make selftest + ./zerotier-selftest + + build_macos: + runs-on: macos-latest + steps: + - name: gitconfig + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - name: checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: aarch64-apple-darwin + override: true + components: rustfmt, clippy + - name: Set up cargo cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + - name: make + run: make + - name: selftest + run: | + make selftest + ./zerotier-selftest build_windows: runs-on: windows-latest @@ -39,13 +79,30 @@ jobs: run: | git config --global core.autocrlf false git config --global core.eol lf - - name: checkout uses: actions/checkout@v3 - + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: aarch64-apple-darwin + override: true + components: rustfmt, clippy + - name: Set up cargo cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- - name: setup msbuild uses: microsoft/setup-msbuild@v1.1.3 - - name: msbuild run: | - msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild + msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne:Rebuild + From 4959d8079bec7f9cf3dcad8224bf0b4db955bd1b Mon Sep 17 00:00:00 2001 From: travis laduke Date: Mon, 19 Dec 2022 13:26:29 -0800 Subject: [PATCH 14/17] enable ICMP in windows firewall --- osdep/WinFWHelper.cpp | 172 ++++++++++++++++++ osdep/WinFWHelper.hpp | 31 ++++ service/OneService.cpp | 19 +- windows/ZeroTierOne/ZeroTierOne.vcxproj | 2 + .../ZeroTierOne/ZeroTierOne.vcxproj.filters | 6 + 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 osdep/WinFWHelper.cpp create mode 100644 osdep/WinFWHelper.hpp diff --git a/osdep/WinFWHelper.cpp b/osdep/WinFWHelper.cpp new file mode 100644 index 000000000..40f38977e --- /dev/null +++ b/osdep/WinFWHelper.cpp @@ -0,0 +1,172 @@ +#include "WinFWHelper.hpp" + + +namespace ZeroTier { + + + +void ZeroTier::WinFWHelper::newICMPRule(const InetAddress& ip, uint64_t nwid) +{ + char nwString[32] = { 0 }; + char ipbuf[64]; + + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + ip.toString(ipbuf); + + if (ip.isV4()) { + WinFWHelper::newICMPv4Rule(ipbuf, nwid); + } + else { + WinFWHelper::newICMPv6Rule(ipbuf, nwid); + } +} + +void ZeroTier::WinFWHelper::removeICMPRule(const InetAddress& ip, uint64_t nwid) +{ + char nwString[32] = { 0 }; + char ipbuf[64]; + + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + ip.toString(ipbuf); + + if (ip.isV4()) { + WinFWHelper::removeICMPv4Rule(ipbuf, nwid); + } + else { + WinFWHelper::removeICMPv6Rule(ipbuf, nwid); + } +} + + +void WinFWHelper::newICMPv4Rule(std::string address, uint64_t nwid) +{ + // allows icmp, scoped to a specific ip address and interface name + + char nwString[32] = { 0 }; + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "New-NetFirewallRule -DisplayName zerotier-icmpv4-)" + nwString2 + address + + R"( -InterfaceAlias 'ZeroTier One `[)" + nwString2 + R"(`]')" + + " -Protocol ICMPv4 -Action Allow" + + " -LocalAddress " + address + "\"\r\n"; + + _run(cmd); +} + +void WinFWHelper::newICMPv6Rule(std::string address, uint64_t nwid) +{ + // allows icmp, scoped to a specific ip address and interface name + + char nwString[32] = { 0 }; + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "New-NetFirewallRule -DisplayName zerotier-icmpv6-)" + nwString2 + address + + R"( -InterfaceAlias 'ZeroTier One `[)" + nwString2 + R"(`]')" + + " -Protocol ICMPv6 -Action Allow" + + " -LocalAddress " + address + "\"\r\n"; + + _run(cmd); +} + +void WinFWHelper::removeICMPv4Rule(std::string addr, uint64_t nwid) +{ + // removes 1 icmp firewall rule + + char nwString[32] = { 0 }; + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv4-)" + nwString2 + addr + + "\"\r\n"; + + _run(cmd); +} + +void WinFWHelper::removeICMPv6Rule(std::string addr, uint64_t nwid) +{ + // removes 1 icmp firewall rule + + char nwString[32] = { 0 }; + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv6-)" + nwString2 + addr + + "\"\r\n"; + + _run(cmd); +} + +void WinFWHelper::removeICMPv4Rules(uint64_t nwid) +{ + // removes all icmp firewall rules for this network id + + char nwString[32] = { 0 }; + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv4-)" + nwString2 + "*\" \r\n"; + + _run(cmd); +} + +void WinFWHelper::removeICMPv6Rules(uint64_t nwid) +{ + // removes all icmp firewall rules for this network id + + char nwString[32] = { 0 }; + sprintf(nwString, "%.16llx", nwid); + std::string nwString2 = { nwString }; + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv6-)" + nwString2 + "*\" \r\n"; + + _run(cmd); +} + +void WinFWHelper::removeICMPRules() +{ + // removes all icmp firewall rules for all networks + + std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmp*)" + std::string("\r\n"); + + _run(cmd); +} + +void WinFWHelper::removeICMPRules(uint64_t nwid) +{ + // removes all icmp firewall rules for this network + WinFWHelper::removeICMPv4Rules(nwid); + WinFWHelper::removeICMPv6Rules(nwid); +} + + + +void WinFWHelper::_run(std::string cmd) +{ + + #ifdef ZT_DEBUG + fprintf(stderr, cmd.c_str()); + #endif + + STARTUPINFOA startupInfo; + PROCESS_INFORMATION processInfo; + startupInfo.cb = sizeof(startupInfo); + memset(&startupInfo, 0, sizeof(STARTUPINFOA)); + memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); + + if (CreateProcessA(NULL, (LPSTR)cmd.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo)) { + WaitForSingleObject(processInfo.hProcess, INFINITE); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } +} + + + +} // namespace ZeroTier \ No newline at end of file diff --git a/osdep/WinFWHelper.hpp b/osdep/WinFWHelper.hpp new file mode 100644 index 000000000..a8d9e27aa --- /dev/null +++ b/osdep/WinFWHelper.hpp @@ -0,0 +1,31 @@ +#ifndef WIN_FW_HELPER_H_ +#define WIN_FW_HELPER_H_ + +#include "../node/InetAddress.hpp" + +#include +#include + +namespace ZeroTier { + +class WinFWHelper { + public: + static void newICMPRule(const InetAddress& ip, uint64_t nwid); + static void removeICMPRule(const InetAddress& ip, uint64_t nwid); + static void removeICMPRules(uint64_t nwid); + static void removeICMPRules(); + + + private: + static void _run(std::string cmd); + static void newICMPv4Rule(std::string address, uint64_t nwid); + static void newICMPv6Rule(std::string address, uint64_t nwid); + static void removeICMPv4Rule(std::string address, uint64_t nwid); + static void removeICMPv6Rule(std::string address, uint64_t nwid); + static void removeICMPv4Rules(uint64_t nwid); + static void removeICMPv6Rules(uint64_t nwid); +}; + +} // namespace ZeroTier + +#endif \ No newline at end of file diff --git a/service/OneService.cpp b/service/OneService.cpp index 5984b8b86..3dd344e0e 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -78,6 +78,7 @@ #include "../osdep/MacDNSHelper.hpp" #elif defined(__WINDOWS__) #include "../osdep/WinDNSHelper.hpp" +#include "../osdep/WinFWHelper.hpp" #endif #ifdef ZT_USE_SYSTEM_HTTP_PARSER @@ -847,6 +848,9 @@ public: virtual ~OneServiceImpl() { +#ifdef __WINDOWS__ + WinFWHelper::removeICMPRules(); +#endif _binder.closeAll(_phy); _phy.close(_localControlSocket4); _phy.close(_localControlSocket6); @@ -855,6 +859,8 @@ public: curl_global_cleanup(); #endif + + #ifdef ZT_USE_MINIUPNPC delete _portMapper; #endif @@ -899,6 +905,7 @@ public: _node = new Node(this,(void *)0,&cb,OSUtils::now()); } + // local.conf readLocalSettings(); applyLocalConfig(); @@ -2262,6 +2269,10 @@ public: if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) { if (!n.tap()->removeIp(*ip)) fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf)); + + #ifdef __WINDOWS__ + WinFWHelper::removeICMPRule(*ip, n.config().nwid); + #endif } } @@ -2269,6 +2280,10 @@ public: if (std::find(n.managedIps().begin(),n.managedIps().end(),*ip) == n.managedIps().end()) { if (!n.tap()->addIp(*ip)) fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf)); + + #ifdef __WINDOWS__ + WinFWHelper::newICMPRule(*ip, n.config().nwid); + #endif } } @@ -2749,8 +2764,10 @@ public: n.tap().reset(); _nets.erase(nwid); #if defined(__WINDOWS__) && !defined(ZT_SDK) - if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0)) + if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) && (winInstanceId.length() > 0)) { WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str()); + WinFWHelper::removeICMPRules(nwid); + } #endif if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) { char nlcpath[256]; diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index 8b2712d3f..36e50ffac 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -124,6 +124,7 @@ + true true @@ -241,6 +242,7 @@ + diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index ebdd33267..ecf664f82 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -288,6 +288,9 @@ Source Files\node + + Source Files\osdep + @@ -551,6 +554,9 @@ Header Files\osdep + + Header Files\osdep + From 91bae4b1a8708aae0c21aca8266fb15147e9de22 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 22 Dec 2022 10:08:02 -0800 Subject: [PATCH 15/17] Add missing default initialization of _lowBandwidthMode --- node/Node.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/Node.cpp b/node/Node.cpp index 7bdf331dc..46f3a4add 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -50,7 +50,8 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64 _lastPingCheck(0), _lastGratuitousPingCheck(0), _lastHousekeepingRun(0), - _lastMemoizedTraceSettings(0) + _lastMemoizedTraceSettings(0), + _lowBandwidthMode(false) { if (callbacks->version != 0) throw ZT_EXCEPTION_INVALID_ARGUMENT; From a6742b7f82c875572eee696de796fac906dccb57 Mon Sep 17 00:00:00 2001 From: travis laduke Date: Fri, 23 Dec 2022 16:09:53 -0800 Subject: [PATCH 16/17] Prevent shadowing VM routes as default route (macOS) If you have a VM host like parallels, sometimes you get these link-local default routes: ``` netstat -nrfinet | grep "default\|\/1" 0/1 10.2.0.12 UGScg feth4823 default 192.168.82.1 UGScg en1 0/1 192.168.82.1 UGScIg en1 default link#22 UCSIg bridge101 ! 128.0/1 10.2.0.12 UGSc feth4823 128.0/1 192.168.82.1 UGScI en1 ``` (the link#22 one) The _getRTEs function inclused these routes in the list it makes as like: device: bridge101, target: 0.0.0.0/0 If it happens to be first in the list, bridge101 gets selected as the default route. Then Full Tunnel Mode doesn't work. The other routes in the list are like: device: en1 target: 192.168.1.0/24 via: metric: 0 ifscope: 0 device: en1 target: 192.168.1.1/32 via: metric: 0 ifscope: 0 We only need the device name from this, so either one will work. --- osdep/ManagedRoute.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp index 325f4c803..a8f996839 100644 --- a/osdep/ManagedRoute.cpp +++ b/osdep/ManagedRoute.cpp @@ -477,7 +477,7 @@ bool ManagedRoute::sync() if ((newSystemVia)&&(!newSystemDevice[0])) { rtes = _getRTEs(newSystemVia,true); for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) { - if ( (r->device[0]) && (strcmp(r->device,_device) != 0) ) { + if ( (r->device[0]) && (strcmp(r->device,_device) != 0) && r->target.netmaskBits() != 0) { Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device); break; } From 27c26a77a25c201221eff694083651a7fe8ff56b Mon Sep 17 00:00:00 2001 From: Sean OMeara Date: Sat, 7 Jan 2023 21:28:12 +0100 Subject: [PATCH 17/17] running build workflow on pull_requests --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f76d5b67..ddd6dd448 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -on: [ push ] +on: [ push, pull_request ] jobs: build_ubuntu: