diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..ddd6dd448 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,108 @@ +on: [ push, pull_request ] + +jobs: + build_ubuntu: + runs-on: ubuntu-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_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 + 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: 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 + diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 23f97b388..a12b3291b 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -384,7 +384,7 @@ enum ZT_ResultCode */ ZT_RESULT_OK_IGNORED = 1, - // Fatal errors (>100, <1000) + // Fatal errors (>=100, <1000) /** * Ran out of memory diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp index bfb969abb..c479f87e2 100644 --- a/java/jni/ZT_jniutils.cpp +++ b/java/jni/ZT_jniutils.cpp @@ -43,7 +43,7 @@ jobject createResultObject(JNIEnv *env, ZT_ResultCode code) resultClass = lookup.findClass("com/zerotier/sdk/ResultCode"); if(resultClass == NULL) { - LOGE("Couldnt find ResultCode class"); + LOGE("Couldn't find ResultCode class"); return NULL; // exception thrown } @@ -1032,4 +1032,3 @@ jobject newVirtualNetworkDNS(JNIEnv *env, const ZT_VirtualNetworkDNS &dns) #ifdef __cplusplus } #endif - diff --git a/java/src/com/zerotier/sdk/ResultCode.java b/java/src/com/zerotier/sdk/ResultCode.java index 66f575613..09e7d3b13 100644 --- a/java/src/com/zerotier/sdk/ResultCode.java +++ b/java/src/com/zerotier/sdk/ResultCode.java @@ -41,7 +41,7 @@ public enum ResultCode { */ RESULT_OK(0), - // Fatal errors (> 0, < 1000) + // Fatal errors (>=100, <1000) /** * Ran out of memory */ diff --git a/node/Bond.cpp b/node/Bond.cpp index dc17e6bee..8edd7e297 100644 --- a/node/Bond.cpp +++ b/node/Bond.cpp @@ -184,7 +184,7 @@ SharedPtr Bond::getLinkBySocket(const std::string& policyAlias, uint64_t l auto search = _interfaceToLinkMap[policyAlias].find(ifnameStr); if (search == _interfaceToLinkMap[policyAlias].end()) { if (createIfNeeded) { - SharedPtr s = new Link(ifnameStr, 0, 0, true, ZT_BOND_SLAVE_MODE_PRIMARY, ""); + SharedPtr s = new Link(ifnameStr, 0, 0, 0, true, ZT_BOND_SLAVE_MODE_PRIMARY, ""); _interfaceToLinkMap[policyAlias].insert(std::pair >(ifnameStr, s)); return s; } @@ -1253,6 +1253,7 @@ void Bond::estimatePathQuality(int64_t now) if (link) { int linkSpeed = link->capacity(); _paths[i].p->_givenLinkSpeed = linkSpeed; + _paths[i].p->_mtu = link->mtu(); maxObservedLinkCap = linkSpeed > maxObservedLinkCap ? linkSpeed : maxObservedLinkCap; } } diff --git a/node/Bond.hpp b/node/Bond.hpp index 3419c8cfe..389b970e9 100644 --- a/node/Bond.hpp +++ b/node/Bond.hpp @@ -122,9 +122,10 @@ class Link { * @param mode * @param failoverToLinkStr */ - Link(std::string ifnameStr, uint8_t ipvPref, uint32_t capacity, bool enabled, uint8_t mode, std::string failoverToLinkStr) + Link(std::string ifnameStr, uint8_t ipvPref, uint16_t mtu, uint32_t capacity, bool enabled, uint8_t mode, std::string failoverToLinkStr) : _ifnameStr(ifnameStr) , _ipvPref(ipvPref) + , _mtu(mtu) , _capacity(capacity) , _relativeCapacity(0.0) , _enabled(enabled) @@ -226,6 +227,14 @@ class Link { return _ipvPref; } + /** + * @return The MTU for this link (as specified by the user.) + */ + inline uint16_t mtu() + { + return _mtu; + } + /** * @return The mode (e.g. primary/spare) for this link (as specified by the user.) */ @@ -260,6 +269,11 @@ class Link { */ uint8_t _ipvPref; + /** + * The physical-layer MTU for this link + */ + uint16_t _mtu; + /** * User-specified capacity of this link */ diff --git a/node/MAC.hpp b/node/MAC.hpp index 76ee4bf31..de72a1aed 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -149,7 +149,7 @@ public: /** * Get the ZeroTier address for this MAC on this network (assuming no bridging of course, basic unicast) * - * This just XORs the next-lest-significant 5 bytes of the network ID again to unmask. + * This just XORs the next-least-significant 5 bytes of the network ID again to unmask. * * @param nwid Network ID */ 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..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; @@ -202,6 +203,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 +271,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 +318,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 +327,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 +346,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/Path.hpp b/node/Path.hpp index 8304f27d8..eb268695d 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -92,6 +92,7 @@ public: _valid(true), _eligible(false), _bonded(false), + _mtu(0), _givenLinkSpeed(0), _relativeQuality(0), _latency(0xffff), @@ -112,6 +113,7 @@ public: _valid(true), _eligible(false), _bonded(false), + _mtu(0), _givenLinkSpeed(0), _relativeQuality(0), _latency(0xffff), @@ -334,6 +336,11 @@ public: */ inline unsigned int bonded() const { return _bonded; } + /** + * @return Whether the user-specified MTU for this path (determined by MTU for parent link) + */ + inline unsigned int mtu() const { return _mtu; } + /** * @return Given link capacity as reported by the bonding layer */ @@ -370,6 +377,7 @@ private: volatile bool _valid; volatile bool _eligible; volatile bool _bonded; + volatile uint16_t _mtu; volatile uint32_t _givenLinkSpeed; volatile float _relativeQuality; 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/node/Switch.cpp b/node/Switch.cpp index ae870a278..9a81e532e 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -1009,7 +1009,8 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId) Mutex::Lock _l(peer->_paths_m); for(int i=0;i_paths[i].p && peer->_paths[i].p->alive(now)) { - _sendViaSpecificPath(tPtr,peer,peer->_paths[i].p,now,packet,encrypt,flowId); + uint16_t userSpecifiedMtu = peer->_paths[i].p->mtu(); + _sendViaSpecificPath(tPtr,peer,peer->_paths[i].p, userSpecifiedMtu,now,packet,encrypt,flowId); } } return true; @@ -1025,7 +1026,8 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId) } } if (viaPath) { - _sendViaSpecificPath(tPtr,peer,viaPath,now,packet,encrypt,flowId); + uint16_t userSpecifiedMtu = viaPath->mtu(); + _sendViaSpecificPath(tPtr,peer,viaPath,userSpecifiedMtu,now,packet,encrypt,flowId); return true; } } @@ -1033,12 +1035,15 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId) return false; } -void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr peer,SharedPtr viaPath,int64_t now,Packet &packet,bool encrypt,int32_t flowId) +void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr peer,SharedPtr viaPath,uint16_t userSpecifiedMtu, int64_t now,Packet &packet,bool encrypt,int32_t flowId) { unsigned int mtu = ZT_DEFAULT_PHYSMTU; uint64_t trustedPathId = 0; RR->topology->getOutboundPathInfo(viaPath->address(),mtu,trustedPathId); + if (userSpecifiedMtu > 0) { + mtu = userSpecifiedMtu; + } unsigned int chunkSize = std::min(packet.size(),mtu); packet.setFragmented(chunkSize < packet.size()); diff --git a/node/Switch.hpp b/node/Switch.hpp index 4ca1c0e50..9bb1fbeaf 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -207,7 +207,7 @@ public: private: bool _shouldUnite(const int64_t now,const Address &source,const Address &destination); bool _trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId = ZT_QOS_NO_FLOW); // packet is modified if return is true - void _sendViaSpecificPath(void *tPtr,SharedPtr peer,SharedPtr viaPath,int64_t now,Packet &packet,bool encrypt,int32_t flowId); + void _sendViaSpecificPath(void *tPtr,SharedPtr peer,SharedPtr viaPath,uint16_t userSpecifiedMtu, int64_t now,Packet &packet,bool encrypt,int32_t flowId); const RuntimeEnvironment *const RR; int64_t _lastBeaconResponse; 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; } 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 5cde1922f..429ae20e4 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 @@ -850,6 +851,9 @@ public: virtual ~OneServiceImpl() { +#ifdef __WINDOWS__ + WinFWHelper::removeICMPRules(); +#endif _binder.closeAll(_phy); _phy.close(_localControlSocket4); _phy.close(_localControlSocket6); @@ -858,6 +862,8 @@ public: curl_global_cleanup(); #endif + + #ifdef ZT_USE_MINIUPNPC delete _portMapper; #endif @@ -902,6 +908,7 @@ public: _node = new Node(this,(void *)0,&cb,OSUtils::now()); } + // local.conf readLocalSettings(); applyLocalConfig(); @@ -2068,6 +2075,7 @@ public: bool enabled = OSUtils::jsonInt(link["enabled"],true); uint32_t capacity = OSUtils::jsonInt(link["capacity"],0); uint8_t ipvPref = OSUtils::jsonInt(link["ipvPref"],0); + uint16_t mtu = OSUtils::jsonInt(link["mtu"],0); std::string failoverToStr(OSUtils::jsonString(link["failoverTo"],"")); // Mode std::string linkModeStr(OSUtils::jsonString(link["mode"],"spare")); @@ -2084,7 +2092,7 @@ public: failoverToStr = ""; enabled = false; } - _node->bondController()->addCustomLink(customPolicyStr, new Link(linkNameStr,ipvPref,capacity,enabled,linkMode,failoverToStr)); + _node->bondController()->addCustomLink(customPolicyStr, new Link(linkNameStr,ipvPref,mtu,capacity,enabled,linkMode,failoverToStr)); } std::string linkSelectMethodStr(OSUtils::jsonString(customPolicy["activeReselect"],"optimize")); if (linkSelectMethodStr == "always") { @@ -2132,6 +2140,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)); @@ -2272,6 +2281,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 } } @@ -2279,6 +2292,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 } } @@ -2762,8 +2779,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 +