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
+