Merge branch 'dev' into sso-update

This commit is contained in:
Grant Limberg 2023-01-09 14:02:44 -08:00
commit 293c153850
No known key found for this signature in database
GPG key ID: 8F2F97D3BE8D7735
19 changed files with 750 additions and 21 deletions

108
.github/workflows/build.yml vendored Normal file
View file

@ -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

3
.gitignore vendored
View file

@ -136,4 +136,5 @@ zeroidc/target/
__pycache__ __pycache__
*.pyc *.pyc
*_source.tar.bz2 *_source.tar.bz2
snap/.snapcraft snap/.snapcraft
tcp-proxy/tcp-proxy

View file

@ -113,7 +113,7 @@ MemberNotificationReceiver::MemberNotificationReceiver(PostgreSQL *p, pqxx::conn
: pqxx::notification_receiver(c, channel) : pqxx::notification_receiver(c, channel)
, _psql(p) , _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) { 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 tmp(json::parse(payload));
json &ov = tmp["old_val"]; json &ov = tmp["old_val"];
json &nv = tmp["new_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); fprintf(stderr, "ZT_SSO_PSK: %s\n", ssoPskHex);
#endif #endif
if (ssoPskHex) { 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, // 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. // it will be padded at the end with zeroes. If longer, it'll be truncated.
Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk)); Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk));
@ -1407,7 +1407,7 @@ void PostgreSQL::commitThread()
"sso_enabled = EXCLUDED.sso_enabled", "sso_enabled = EXCLUDED.sso_enabled",
id, id,
_myAddressStr, _myAddressStr,
OSUtils::jsonDump(config["capabilitles"], -1), OSUtils::jsonDump(config["capabilities"], -1),
(bool)config["enableBroadcast"], (bool)config["enableBroadcast"],
OSUtils::now(), OSUtils::now(),
(int)config["mtu"], (int)config["mtu"],
@ -1606,7 +1606,7 @@ void PostgreSQL::onlineNotificationThread()
/** /**
* ONLY UNCOMMENT FOR TEMPORARY DB MAINTENANCE * 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. * so it can be reindexed when the indexes get too large.
*/ */

View file

@ -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 | | vMajor | integer | Most recently known major version | no |
| vMinor | integer | Most recently known minor version | no | | vMinor | integer | Most recently known minor version | no |
| vRev | integer | Most recently known revision | 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. Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored.

View file

@ -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. 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): * `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): * `networks.d` (directory):
This caches network configurations and certificate information for networks you belong to. ZeroTier scans this directory for <network ID>.conf files on startup to recall its networks, so "touch"ing an empty <network ID>.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. This caches network configurations and certificate information for networks you belong to. ZeroTier scans this directory for <network ID>.conf files on startup to recall its networks, so "touch"ing an empty <network ID>.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.

View file

@ -268,7 +268,8 @@ void Multicaster::send(
const unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1; 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; gs.lastExplicitGather = now;
Address explicitGatherPeers[16]; Address explicitGatherPeers[16];

View file

@ -50,7 +50,8 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64
_lastPingCheck(0), _lastPingCheck(0),
_lastGratuitousPingCheck(0), _lastGratuitousPingCheck(0),
_lastHousekeepingRun(0), _lastHousekeepingRun(0),
_lastMemoizedTraceSettings(0) _lastMemoizedTraceSettings(0),
_lowBandwidthMode(false)
{ {
if (callbacks->version != 0) if (callbacks->version != 0)
throw ZT_EXCEPTION_INVALID_ARGUMENT; throw ZT_EXCEPTION_INVALID_ARGUMENT;
@ -202,6 +203,14 @@ public:
{ {
const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address()); const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address());
if (alwaysContactEndpoints) { 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); const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now);
bool contacted = (sent != 0); 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; const int64_t timeSinceLastPingCheck = now - _lastPingCheck;
if (timeSinceLastPingCheck >= timeUntilNextPingCheck) { if (timeSinceLastPingCheck >= timeUntilNextPingCheck) {
try { 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 // 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 // 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<Network>,bool > > networkConfigNeeded; std::vector< std::pair< SharedPtr<Network>,bool > > networkConfigNeeded;
{ {
Mutex::Lock l(_networks_m); Mutex::Lock l(_networks_m);
@ -317,7 +327,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64
SharedPtr<Network> *network = (SharedPtr<Network> *)0; SharedPtr<Network> *network = (SharedPtr<Network> *)0;
while (i.next(nwid,network)) { while (i.next(nwid,network)) {
(*network)->config().alwaysContactAddresses(alwaysContact); (*network)->config().alwaysContactAddresses(alwaysContact);
networkConfigNeeded.push_back( std::pair< SharedPtr<Network>,bool >(*network,(((now - (*network)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*network)->hasConfig()))) ); networkConfigNeeded.push_back( std::pair< SharedPtr<Network>,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 // Refresh network config or broadcast network updates to members as needed
for(std::vector< std::pair< SharedPtr<Network>,bool > >::const_iterator n(networkConfigNeeded.begin());n!=networkConfigNeeded.end();++n) { for(std::vector< std::pair< SharedPtr<Network>,bool > >::const_iterator n(networkConfigNeeded.begin());n!=networkConfigNeeded.end();++n) {
if (n->second) if (n->second) {
n->first->requestConfiguration(tptr); n->first->requestConfiguration(tptr);
n->first->sendUpdatesToMembers(tptr); }
if (! _lowBandwidthMode) {
n->first->sendUpdatesToMembers(tptr);
}
} }
// Update online status, post status change as event // Update online status, post status change as event

View file

@ -269,6 +269,16 @@ public:
_stats.inVerbBytes[v] += (uint64_t)bytes; _stats.inVerbBytes[v] += (uint64_t)bytes;
} }
inline void setLowBandwidthMode(bool isEnabled)
{
_lowBandwidthMode = isEnabled;
}
inline bool lowBandwidthModeEnabled()
{
return _lowBandwidthMode;
}
private: private:
RuntimeEnvironment _RR; RuntimeEnvironment _RR;
RuntimeEnvironment *RR; RuntimeEnvironment *RR;
@ -316,6 +326,7 @@ private:
int64_t _lastMemoizedTraceSettings; int64_t _lastMemoizedTraceSettings;
volatile int64_t _prngState[2]; volatile int64_t _prngState[2];
bool _online; bool _online;
bool _lowBandwidthMode;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -219,11 +219,15 @@ void Peer::received(
// is done less frequently. // is done less frequently.
if (this->trustEstablished(now)) { if (this->trustEstablished(now)) {
const int64_t sinceLastPush = now - _lastDirectPathPushSent; 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; _lastDirectPathPushSent = now;
std::vector<InetAddress> pathsToPush(RR->node->directPaths()); std::vector<InetAddress> pathsToPush(RR->node->directPaths());
std::vector<InetAddress> ma = RR->sa->whoami(); if (! lowBandwidth) {
pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end()); std::vector<InetAddress> ma = RR->sa->whoami();
pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end());
}
if (!pathsToPush.empty()) { if (!pathsToPush.empty()) {
std::vector<InetAddress>::const_iterator p(pathsToPush.begin()); std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
while (p != pathsToPush.end()) { while (p != pathsToPush.end()) {
@ -453,7 +457,7 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA
if (atAddress) { if (atAddress) {
outp.armor(_key,false,nullptr); // false == don't encrypt full payload, but add MAC outp.armor(_key,false,nullptr); // false == don't encrypt full payload, but add MAC
RR->node->expectReplyTo(outp.packetId()); 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 { } else {
RR->node->expectReplyTo(outp.packetId()); RR->node->expectReplyTo(outp.packetId());
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC 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) { if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
_lastTriedMemorizedPath = now; _lastTriedMemorizedPath = now;
InetAddress mp; 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); attemptToContactAt(tPtr,-1,mp,now,true);
}
} }
} }

View file

@ -302,6 +302,8 @@ public:
*/ */
inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); } 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 * @return Latency in milliseconds of best/aggregate path or 0xffff if unknown / no paths
*/ */

View file

@ -477,7 +477,7 @@ bool ManagedRoute::sync()
if ((newSystemVia)&&(!newSystemDevice[0])) { if ((newSystemVia)&&(!newSystemDevice[0])) {
rtes = _getRTEs(newSystemVia,true); rtes = _getRTEs(newSystemVia,true);
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) { 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); Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
break; break;
} }

172
osdep/WinFWHelper.cpp Normal file
View file

@ -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

31
osdep/WinFWHelper.hpp Normal file
View file

@ -0,0 +1,31 @@
#ifndef WIN_FW_HELPER_H_
#define WIN_FW_HELPER_H_
#include "../node/InetAddress.hpp"
#include <cstdint>
#include <vector>
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

View file

@ -78,6 +78,7 @@
#include "../osdep/MacDNSHelper.hpp" #include "../osdep/MacDNSHelper.hpp"
#elif defined(__WINDOWS__) #elif defined(__WINDOWS__)
#include "../osdep/WinDNSHelper.hpp" #include "../osdep/WinDNSHelper.hpp"
#include "../osdep/WinFWHelper.hpp"
#endif #endif
#ifdef ZT_USE_SYSTEM_HTTP_PARSER #ifdef ZT_USE_SYSTEM_HTTP_PARSER
@ -849,6 +850,9 @@ public:
virtual ~OneServiceImpl() virtual ~OneServiceImpl()
{ {
#ifdef __WINDOWS__
WinFWHelper::removeICMPRules();
#endif
_binder.closeAll(_phy); _binder.closeAll(_phy);
_phy.close(_localControlSocket4); _phy.close(_localControlSocket4);
_phy.close(_localControlSocket6); _phy.close(_localControlSocket6);
@ -857,6 +861,8 @@ public:
curl_global_cleanup(); curl_global_cleanup();
#endif #endif
#ifdef ZT_USE_MINIUPNPC #ifdef ZT_USE_MINIUPNPC
delete _portMapper; delete _portMapper;
#endif #endif
@ -901,6 +907,7 @@ public:
_node = new Node(this,(void *)0,&cb,OSUtils::now()); _node = new Node(this,(void *)0,&cb,OSUtils::now());
} }
// local.conf // local.conf
readLocalSettings(); readLocalSettings();
applyLocalConfig(); applyLocalConfig();
@ -2124,6 +2131,7 @@ public:
fprintf(stderr,"WARNING: using manually-specified secondary and/or tertiary ports. This can cause NAT issues." ZT_EOL_S); 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); _portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true);
_node->setLowBandwidthMode(OSUtils::jsonBool(settings["lowBandwidthMode"],false));
#ifndef ZT_SDK #ifndef ZT_SDK
const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT)); const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT));
@ -2264,6 +2272,10 @@ public:
if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) { if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
if (!n.tap()->removeIp(*ip)) if (!n.tap()->removeIp(*ip))
fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf)); fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf));
#ifdef __WINDOWS__
WinFWHelper::removeICMPRule(*ip, n.config().nwid);
#endif
} }
} }
@ -2271,6 +2283,10 @@ public:
if (std::find(n.managedIps().begin(),n.managedIps().end(),*ip) == n.managedIps().end()) { if (std::find(n.managedIps().begin(),n.managedIps().end(),*ip) == n.managedIps().end()) {
if (!n.tap()->addIp(*ip)) if (!n.tap()->addIp(*ip))
fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf)); fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
#ifdef __WINDOWS__
WinFWHelper::newICMPRule(*ip, n.config().nwid);
#endif
} }
} }
@ -2751,8 +2767,10 @@ public:
n.tap().reset(); n.tap().reset();
_nets.erase(nwid); _nets.erase(nwid);
#if defined(__WINDOWS__) && !defined(ZT_SDK) #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()); WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str());
WinFWHelper::removeICMPRules(nwid);
}
#endif #endif
if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) { if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
char nlcpath[256]; char nlcpath[256];

7
tcp-proxy/Makefile Normal file
View file

@ -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

35
tcp-proxy/README.md Normal file
View file

@ -0,0 +1,35 @@
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
```

317
tcp-proxy/tcp-proxy.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
// 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 <linux/posix_types.h>
#include <bits/types.h>
#undef __FD_SETSIZE
#define __FD_SETSIZE 1048576
#undef FD_SETSIZE
#define FD_SETSIZE 1048576
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <map>
#include <set>
#include <string>
#include <algorithm>
#include <vector>
#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<TcpProxyService *> *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<struct sockaddr *>(&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<len;++i)
c.tcpWriteBuf[c.tcpWritePtr++] = ((const char *)data)[i];
}
printf("<< UDP %s:%d -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(int)ntohs(reinterpret_cast<const struct sockaddr_in *>(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<const struct sockaddr_in *>(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<len;++i) {
if (c.tcpReadPtr >= 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<PhySocket *> 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<PhySocket *>::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<TcpProxyService *> 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;
}

View file

@ -124,6 +124,7 @@
<ClCompile Include="..\..\osdep\PortMapper.cpp" /> <ClCompile Include="..\..\osdep\PortMapper.cpp" />
<ClCompile Include="..\..\osdep\WinDNSHelper.cpp" /> <ClCompile Include="..\..\osdep\WinDNSHelper.cpp" />
<ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" /> <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
<ClCompile Include="..\..\osdep\WinFWHelper.cpp" />
<ClCompile Include="..\..\selftest.cpp"> <ClCompile Include="..\..\selftest.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -241,6 +242,7 @@
<ClInclude Include="..\..\osdep\Thread.hpp" /> <ClInclude Include="..\..\osdep\Thread.hpp" />
<ClInclude Include="..\..\osdep\WinDNSHelper.hpp" /> <ClInclude Include="..\..\osdep\WinDNSHelper.hpp" />
<ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" /> <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
<ClInclude Include="..\..\osdep\WinFWHelper.hpp" />
<ClInclude Include="..\..\service\OneService.hpp" /> <ClInclude Include="..\..\service\OneService.hpp" />
<ClInclude Include="..\..\service\SoftwareUpdater.hpp" /> <ClInclude Include="..\..\service\SoftwareUpdater.hpp" />
<ClInclude Include="..\..\version.h" /> <ClInclude Include="..\..\version.h" />

View file

@ -288,6 +288,9 @@
<ClCompile Include="..\..\node\AES_armcrypto.cpp"> <ClCompile Include="..\..\node\AES_armcrypto.cpp">
<Filter>Source Files\node</Filter> <Filter>Source Files\node</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\osdep\WinFWHelper.cpp">
<Filter>Source Files\osdep</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="resource.h"> <ClInclude Include="resource.h">
@ -551,6 +554,9 @@
<ClInclude Include="..\..\osdep\WinDNSHelper.hpp"> <ClInclude Include="..\..\osdep\WinDNSHelper.hpp">
<Filter>Header Files\osdep</Filter> <Filter>Header Files\osdep</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\osdep\WinFWHelper.hpp">
<Filter>Header Files\osdep</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="ZeroTierOne.rc"> <ResourceCompile Include="ZeroTierOne.rc">