Merge branch 'dev' into redisrection

This commit is contained in:
Grant Limberg 2022-06-13 13:09:36 -07:00
commit c6fc3560f2
No known key found for this signature in database
GPG key ID: 8F2F97D3BE8D7735
12804 changed files with 2946788 additions and 1833 deletions

68
.drone.jsonnet Normal file
View file

@ -0,0 +1,68 @@
local targets = [
//
// Render these into .drone.yaml by running "make drone"
//
{ "os": "linux", "name": "el9", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "el8", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "el7", "isas": [ "amd64", "ppc64le"], "events": [ "tag" ] },
{ "os": "linux", "name": "el6", "isas": [ "amd64" ], "events": [ "tag" ] },
{ "os": "linux", "name": "amzn2", "isas": [ "amd64", "arm64" ], "events": [ "tag" ] },
{ "os": "linux", "name": "fc37", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "fc36", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "fc35", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "jammy", "isas": [ "amd64", "arm64", "armv7", "riscv64", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "focal", "isas": [ "amd64", "arm64", "armv7", "riscv64", "ppc64le" ], "events": [ "tag" ] },
{ "os": "linux", "name": "bionic", "isas": [ "amd64", "arm64", "386", "ppc64le", "s390x" ], "events": ["tag" ] },
{ "os": "linux", "name": "xenial", "isas": [ "amd64", "arm64", "386" ], "events": [ "tag" ] },
{ "os": "linux", "name": "sid", "isas": [ "386", "amd64", "arm64", "armv7", "riscv64", "mips64le", "ppc64le", "s390x" ], "events": [ "push", "tag" ] },
{ "os": "linux", "name": "bookworm", "isas": [ "amd64", "arm64", "armv7", "386", "mips64le", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "bullseye", "isas": [ "amd64", "arm64", "armv7", "386", "mips64le", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "buster", "isas": [ "amd64", "arm64", "armv7", "386", "mips64le", "ppc64le", "s390x" ], "events": [ "tag" ] },
{ "os": "linux", "name": "stretch", "isas": [ "amd64", "arm64", "386" ], "events": [ "tag" ] },
// { "os": "windows", "name": "win2k19", "isas": [ "amd64" ], "events": ["push", "tag" ] }
];
local Build(platform, os, isa, events) = {
"kind": "pipeline",
"type": "docker",
"pull": "always",
"name": platform + " " + isa + " " + "build",
"clone": { "depth": 1 },
"steps": [
{
"name": "build",
"image": "registry.sean.farm/honda-builder",
"commands": [ "./ci/scripts/build.sh " + platform + " " + isa + " " + "100.0.0+${DRONE_COMMIT_SHA:0:8}" + " " + "${DRONE_BUILD_EVENT}" ]
},
{
"name": "list",
"image": "registry.sean.farm/honda-builder",
"commands": [ "ls -la " + platform ]
},
{
"name": "notify-mattermost",
"image": "registry.sean.farm/mattermost-notify",
"environment": {
"token": { "from_secret": "mattermost-token" },
"host": { "from_secret": "mattermost-host" },
"channel": { "from_secret": "mattermost-channel" },
"maxRetry": 3,
},
"when": { "status": [ "failure" ] }
}
],
"image_pull_secrets": [ "dockerconfigjson" ],
[ if isa == "arm64" || isa == "armv7" then "platform" ]: { os: os, arch: "arm64" },
"trigger": { "event": events }
};
// puttin on the bits
std.flattenArrays([
[
Build(p.name, p.os, isa, p.events)
for isa in p.isas
]
for p in targets
])

2844
.drone.yml

File diff suppressed because it is too large Load diff

10
.gitignore vendored
View file

@ -6,6 +6,11 @@
/zerotier
/nltest
# IDE stuff
/.idea
/.nova
/compile_commands.json
# OS-created garbage files from various platforms
.DS_Store
.Apple*
@ -30,7 +35,6 @@ Thumbs.db
/windows/WebUIWrapper/obj
/windows/lib
/ext/installfiles/windows/ZeroTier One-SetupFiles
/ext/installfiles/windows/Prerequisites
/ext/installfiles/windows/*-cache
/ZeroTier One.msi
*.vcxproj.backup
@ -58,7 +62,6 @@ zt1-src.tar.gz
*.opensdf
*.user
*.cache
*.obj
*.tlog
*.pid
*.pkg
@ -103,7 +106,6 @@ windows/ZeroTierOne/Debug/
*.swp
*~.nib
DerivedData/
build/
*.pbxuser
*.mode1v3
*.mode2v3
@ -114,7 +116,6 @@ build/
!default.perspectivev3
*.xccheckout
xcuserdata/
ext/librethinkdbxx/build
.vscode
__pycache__
*~
@ -123,7 +124,6 @@ attic/world/mkworld
workspace/
workspace2/
zeroidc/target/
tmp/
#snapcraft specifics
/parts/

View file

@ -26,3 +26,7 @@ endif
ifeq ($(OSTYPE),NetBSD)
include make-netbsd.mk
endif
drone:
@echo "rendering .drone.yaml from .drone.jsonnet"
drone jsonnet --format --stream

View file

@ -1,6 +1,7 @@
ZeroTier - Global Area Networking
======
This document is written for a software developer audience. For information on using ZeroTier, see the: [Website](https://www.zerotier.com), [Documentation Site](https://docs.zerotier.com), and [Discussion Forum](https://discuss.zerotier.com)
*This document is written for a software developer audience. For information on using ZeroTier, see the: [Website](https://www.zerotier.com), [Documentation Site](https://docs.zerotier.com), and [Discussion Forum](https://discuss.zerotier.com).*
ZeroTier is a smart programmable Ethernet switch for planet Earth. It allows all networked devices, VMs, containers, and applications to communicate as if they all reside in the same physical data center or cloud region.
@ -42,24 +43,29 @@ The base path contains the ZeroTier One service main entry point (`one.cpp`), se
- `rule-compiler/`: JavaScript rules language compiler for defining network-level rules.
- `service/`: the ZeroTier One service, which wraps the ZeroTier core and provides VPN-like connectivity to virtual networks for desktops, laptops, servers, VMs, and containers.
- `windows/`: Visual Studio solution files, Windows service code, and the Windows task bar app UI.
- `zeroidc/`: OIDC implementation used by ZeroTier service to log into SSO-enabled networks. (This part is written in Rust, and more Rust will be appearing in this repository in the future.)
### Build and Platform Notes
To build on Mac and Linux just type `make`. On FreeBSD and OpenBSD `gmake` (GNU make) is required and can be installed from packages or ports. For Windows there is a Visual Studio solution in `windows/`.
- **Mac**
- Xcode command line tools for OSX 10.8 or newer are required.
- Xcode command line tools for macOS 10.13 or newer are required.
- Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*.
- **Linux**
- The minimum compiler versions required are GCC/G++ 4.9.3 or CLANG/CLANG++ 3.4.2. (Install `clang` on CentOS 7 as G++ is too old.)
- Linux makefiles automatically detect and prefer clang/clang++ if present as it produces smaller and slightly faster binaries in most cases. You can override by supplying CC and CXX variables on the make command line.
- Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*.
- **Windows**
- Windows 7 or newer is supported. This *may* work on Vista but isn't officially supported there. It will not work on Windows XP.
- We build with Visual Studio 2017. Older versions may not work. Clang or MinGW will also probably work but may require some makefile hacking.
- Visual Studio 2022 on Windows 10 or newer.
- Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*.
- **FreeBSD**
- GNU make is required. Type `gmake` to build.
- Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*.
- **OpenBSD**
- There is a limit of four network memberships on OpenBSD as there are only four tap devices (`/dev/tap0` through `/dev/tap3`).
- GNU make is required. Type `gmake` to build.
- Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*.
Typing `make selftest` will build a *zerotier-selftest* binary which unit tests various internals and reports on a few aspects of the build environment. It's a good idea to try this on novel platforms or architectures.
@ -82,7 +88,7 @@ Here's where home folders live (by default) on each OS:
* **Linux**: `/var/lib/zerotier-one`
* **FreeBSD** / **OpenBSD**: `/var/db/zerotier-one`
* **Mac**: `/Library/Application Support/ZeroTier/One`
* **Windows**: `\ProgramData\ZeroTier\One` (That's for Windows 7. The base 'shared app data' folder might be different on different Windows versions.)
* **Windows**: `\ProgramData\ZeroTier\One` (That's the default. The base 'shared app data' folder might be different if Windows is installed with a non-standard drive letter assignment or layout.)
### Basic Troubleshooting

View file

@ -1,11 +1,40 @@
ZeroTier Release Notes
======
# 2022-03-21 -- Version 1.8.7
# 2022-06-07 -- Version 1.10.0
* Fix for dependency installations in Windows MSI package
* Fix for privilege escalation in desktop UI when the user is not a current super-user
* Bug fix in local OIDC / SSO support
* Fix formatting problem in `zerotier-cli` when using SSO networks.
* Fix a few other minor bugs in SSO signin to prepare for general availability.
* Remove requirement for webview in desktop UI and instead just make everything available via the tray pulldown/menu. Use [libui-ng](https://github.com/libui-ng/libui-ng) for minor prompt dialogs. Saves space and eliminates installation headaches on Windows.
* Fix SSO "spam" bug in desktop UI.
* Use system default browser for SSO login so all your plugins, MFA devices, password managers, etc. will work as you have them configured.
* Minor fix for bonding/multipath.
# 2022-05-10 -- Version 1.8.10
* Fixed a bug preventing SSO sign-on on Windows.
# 2022-04-25 -- Version 1.8.9
* Fixed a long-standing and strange bug that was causing sporadic "phantom" packet authentication failures. Not a security problem but could be behind spordaic reports of link failures under some conditions.
* Fized a memory leak in SSO/OIDC support.
* Fixed SSO/OIDC display error on CLI.
* Fixed a bug causing nodes to sometimes fail to push certs to each other (primarily affects SSO/OIDC use cases).
* Fixed a deadlock bug on leaving SSO/OIDC managed networks.
* Added some new Linux distributions to the build subsystem.
# 2022-04-11 -- Version 1.8.8
* Fix a local privilege escalation bug in the Windows installer.
* Dependency fix for some Ubuntu versions.
* No changes for other platforms. Windows upgrade recommended, everyone else optional.
# 2022-03-30 -- Version 1.8.7
* Fix for dependency installations in Windows MSI package.
* Fix for desktop UI setup when run by a non-super-user.
* Bug fix in local OIDC / SSO support for auth0 and other providers.
* Other minor fixes for e.g. old Linux distributions.
# 2022-03-04 -- Version 1.8.6

10
ci/Dockerfile.deb Normal file
View file

@ -0,0 +1,10 @@
ARG PLATFORM
FROM registry.sean.farm/${PLATFORM}-builder as stage
WORKDIR /work/build
COPY . .
RUN make debian
RUN ls -ls /work
FROM scratch AS export
ARG PLATFORM
COPY --from=stage /work/*.deb ./${PLATFORM}/

36
ci/Dockerfile.el6 Normal file
View file

@ -0,0 +1,36 @@
ARG DOCKER_ARCH
FROM --platform=linux/${DOCKER_ARCH} alpine:edge AS builder
RUN apk update
RUN apk add curl
RUN apk add bash
RUN apk add file
RUN apk add rust
RUN apk add cargo
RUN apk add make
RUN apk add cmake
RUN apk add clang
RUN apk add openssl-dev
RUN apk add linux-headers
RUN apk add build-base
RUN apk add openssl-libs-static
COPY . .
RUN ZT_STATIC=1 make one
RUN ls -la
ARG DOCKER_ARCH
FROM --platform=linux/${DOCKER_ARCH} centos:6 AS stage
WORKDIR /root/rpmbuild/BUILD
COPY . .
COPY --from=builder zerotier-one ./
RUN curl https://gist.githubusercontent.com/someara/b363002ba6e57b3c474dd027d4daef85/raw/4ac5534139752fc92fbe1a53599a390214f69615/el6%2520vault --output /etc/yum.repos.d/CentOS-Base.repo
RUN uname -a
RUN yum -y install make gcc rpm-build
RUN pwd
RUN ls -la
RUN make redhat
FROM scratch AS export
ARG PLATFORM
COPY --from=stage /root/rpmbuild/RPMS/*/*.rpm ./${PLATFORM}/

9
ci/Dockerfile.rpm Normal file
View file

@ -0,0 +1,9 @@
ARG PLATFORM
FROM registry.sean.farm/${PLATFORM}-builder as stage
WORKDIR /root/rpmbuild/BUILD
COPY . .
RUN make redhat
FROM scratch AS export
ARG PLATFORM
COPY --from=stage /root/rpmbuild/RPMS/*/*.rpm ./${PLATFORM}/

View file

@ -1,7 +0,0 @@
FROM registry.sean.farm/sid-builder as stage
COPY . .
RUN /usr/bin/make -j 8
FROM scratch AS export
COPY --from=stage /zerotier-one .
COPY --from=stage /zerotier-cli .

View file

@ -2,27 +2,115 @@
set -euo pipefail
IFS=$'\n\t'
export GOOS=$1
export GOARCH=$2
export PLATFORM=$1
export ZT_ISA=$2
export VERSION=$3
export DOCKER_BUILDKIT=1
export EVENT=$4
echo "nproc: $(nproc)"
case $GOARCH in
armv5)
export ARCH=arm/v5
;;
armv7)
export ARCH=arm/v7
;;
arm64)
export ARCH=arm64/v8
case $PLATFORM in
el*|fc*|amzn*)
export PKGFMT=rpm
;;
*)
export ARCH=$GOARCH
export PKGFMT=deb
esac
# OSX
# x86_64-apple-darwin
# aarch64-apple-darwin
# Windows
# x86_64-pc-windows-msvc
# i686-pc-windows-msvc
# aarch64-pc-windows-msvc
# Linux
# i686-unknown-linux-gnu
# x86_64-unknown-linux-gnu
# arm-unknown-linux-gnueabi ?
# arm-unknown-linux-gnueabihf ?
# armv7-unknown-linux-gnueabihf
#
case $ZT_ISA in
386)
export DOCKER_ARCH=386
export RUST_TRIPLET=i686-unknown-linux-gnu
;;
amd64)
export DOCKER_ARCH=amd64
export RUST_TRIPLET=x86_64-unknown-linux-gnu
;;
armv6)
export DOCKER_ARCH=arm/v6
export RUST_TRIPLET=arm-unknown-linux-gnueabi
;;
armv7)
export DOCKER_ARCH=arm/v7
export RUST_TRIPLET=arm-unknown-linux-gnueabihf
;;
arm64)
export DOCKER_ARCH=arm64/v8
export RUST_TRIPLET=aarch64-unknown-linux-gnu
;;
riscv64)
export DOCKER_ARCH=riscv64
export RUST_TRIPLET=riscv64gc-unknown-linux-gnu
;;
ppc64le)
export DOCKER_ARCH=ppc64le
export RUST_TRIPLET=powerpc64le-unknown-linux-gnu
;;
mips64le)
export DOCKER_ARCH=mips64le
export RUST_TRIPLET=mips64el-unknown-linux-gnuabi64
;;
s390x)
export DOCKER_ARCH=s390x
export RUST_TRIPLET=s390x-unknown-linux-gnu
;;
*)
echo "ERROR: could not determine architecture settings. PLEASE FIX ME"
exit 1
;;
esac
if [ -f "ci/Dockerfile.${PLATFORM}" ]; then
export DOCKERFILE="ci/Dockerfile.${PLATFORM}"
else
export DOCKERFILE="ci/Dockerfile.${PKGFMT}"
fi
echo "#~~~~~~~~~~~~~~~~~~~~"
echo "$0 variables:"
echo "nproc: $(nproc)"
echo "ZT_ISA: ${ZT_ISA}"
echo "DOCKER_ARCH: ${DOCKER_ARCH}"
echo "RUST_TRIPLET: ${RUST_TRIPLET}"
echo "VERSION: ${VERSION}"
echo "EVENT: ${EVENT}"
echo "PKGFMT: ${PKGFMT}"
echo "PWD: ${PWD}"
echo "DOCKERFILE: ${DOCKERFILE}"
echo "#~~~~~~~~~~~~~~~~~~~~"
if [ ${EVENT} == "push" ]; then
make munge_rpm zerotier-one.spec VERSION=${VERSION}
make munge_deb debian/changelog VERSION=${VERSION}
fi
export DOCKER_BUILDKIT=1
docker run --privileged --rm tonistiigi/binfmt --install all
docker buildx build --platform ${GOOS}/${ARCH} -f ci/Dockerfile.sid --target export -t test . --output out/${GOOS}/${GOARCH}
# docker pull --platform linux/${DOCKER_ARCH} registry.sean.farm/${PLATFORM}-builder
docker buildx build \
--build-arg PLATFORM="${PLATFORM}" \
--build-arg RUST_TRIPLET="${RUST_TRIPLET}" \
--build-arg DOCKER_ARCH="${DOCKER_ARCH}" \
--platform linux/${DOCKER_ARCH} \
-f ${DOCKERFILE} \
-t build \
. \
--output type=local,dest=. \
--target export

View file

@ -0,0 +1,37 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
export FILE=$1
export VERSION=$2
export NAME=$3
export MESSAGE=$4
export DATE=$(date "+%a, %d %b %Y %T %z")
# export DATE=$(date "+%a %b %d %Y")
set +e
grep --version | grep BSD &> /dev/null
if [ $? == 0 ]; then BSDGREP=true ; else BSDGREP=false ; fi
set -e
# echo "#~~~~~~~~~~~~~~~~~~~~"
# echo "$0 variables:"
# echo "VERSION: ${VERSION}"
# echo "NAME: ${NAME}"
# echo "MESSAGE: ${MESSAGE}"
# echo "DATE: ${DATE}"
# echo "BSDGREP: ${BSDGREP}"
# echo "#~~~~~~~~~~~~~~~~~~~~"
# echo
if $BSDGREP ; then
sed -i '' s/^Version:.*/"Version: ${VERSION}"/ ${FILE}
else
sed -i s/^Version:.*/"Version: ${VERSION}"/ ${FILE}
fi
awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \
'BEGIN{print "zerotier-one (" version ") unstable; urgency=medium\n\n * " message "\n\n -- " name " " date "\n" }{ print }' \
${FILE} > ${FILE}.new
mv ${FILE}.new ${FILE}

36
ci/scripts/munge_rpm_spec.sh Executable file
View file

@ -0,0 +1,36 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
export FILE=$1
export VERSION=$2
export NAME=$3
export MESSAGE=$4
export DATE=$(date "+%a %b %d %Y")
set +e
grep --version | grep BSD &> /dev/null
if [ $? == 0 ]; then BSDGREP=true ; else BSDGREP=false ; fi
set -e
# echo "#~~~~~~~~~~~~~~~~~~~~"
# echo "$0 variables:"
# echo "VERSION: ${VERSION}"
# echo "NAME: ${NAME}"
# echo "MESSAGE: ${MESSAGE}"
# echo "DATE: ${DATE}"
# echo "BSDGREP: ${BSDGREP}"
# echo "#~~~~~~~~~~~~~~~~~~~~"
# echo
if $BSDGREP ; then
sed -i '' s/^Version:.*/"Version: ${VERSION}"/ ${FILE}
else
sed -i s/^Version:.*/"Version: ${VERSION}"/ ${FILE}
fi
awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \
'FNR==NR{ if (/%changelog/) p=NR; next} 1; FNR==p{ print "* " date " " name " - " version "\n- " message "\n" }' \
${FILE} ${FILE} > ${FILE}.new
mv ${FILE}.new ${FILE}

View file

@ -196,14 +196,6 @@ void DB::networks(std::set<uint64_t> &networks)
networks.insert(n->first);
}
void DB::networkMemberSSOHasExpired(uint64_t nwid, int64_t now) {
std::lock_guard<std::mutex> l(_networks_l);
auto nw = _networks.find(nwid);
if (nw != _networks.end()) {
nw->second->mostRecentDeauthTime = now;
}
}
void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool notifyListeners)
{
uint64_t memberId = 0;

View file

@ -33,7 +33,7 @@
#include <set>
#include <map>
#include "../ext/json/json.hpp"
#include <nlohmann/json.hpp>
#define ZT_MEMBER_AUTH_TIMEOUT_NOTIFY_BEFORE 25000
@ -135,7 +135,6 @@ public:
virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0;
virtual AuthInfo getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL) { return AuthInfo(); }
virtual void networkMemberSSOHasExpired(uint64_t nwid, int64_t ts);
inline void addListener(DB::ChangeListener *const listener)
{

View file

@ -137,14 +137,6 @@ AuthInfo DBMirrorSet::getSSOAuthInfo(const nlohmann::json &member, const std::st
return AuthInfo();
}
void DBMirrorSet::networkMemberSSOHasExpired(uint64_t nwid, int64_t ts)
{
std::lock_guard<std::mutex> l(_dbs_l);
for(auto d=_dbs.begin();d!=_dbs.end();++d) {
(*d)->networkMemberSSOHasExpired(nwid, ts);
}
}
void DBMirrorSet::networks(std::set<uint64_t> &networks)
{
std::lock_guard<std::mutex> l(_dbs_l);
@ -248,47 +240,4 @@ void DBMirrorSet::onNetworkMemberDeauthorize(const void *db,uint64_t networkId,u
_listener->onNetworkMemberDeauthorize(this,networkId,memberId);
}
void DBMirrorSet::membersExpiring(std::set< std::pair<uint64_t, uint64_t> > &soon, std::set< std::pair<uint64_t, uint64_t> > &expired)
{
std::unique_lock<std::mutex> l(_membersExpiringSoon_l);
int64_t now = OSUtils::now();
for(auto next=_membersExpiringSoon.begin();next!=_membersExpiringSoon.end();) {
if (next->first > now) {
const uint64_t nwid = next->second.first;
const uint64_t memberId = next->second.second;
nlohmann::json network, member;
if (this->get(nwid, network, memberId, member)) {
try {
const bool authorized = member["authorized"];
const bool ssoExempt = member["ssoExempt"];
const int64_t authenticationExpiryTime = member["authenticationExpiryTime"];
if ((authenticationExpiryTime == next->first)&&(authorized)&&(!ssoExempt)) {
if ((authenticationExpiryTime - now) > ZT_MEMBER_AUTH_TIMEOUT_NOTIFY_BEFORE) {
// Stop when we get to entries too far in the future.
break;
} else {
const bool ssoEnabled = network["ssoEnabled"];
if (ssoEnabled)
soon.insert(std::pair<uint64_t, uint64_t>(nwid, memberId));
}
} else {
// Obsolete entry, no longer authorized, or SSO exempt.
}
} catch ( ... ) {
// Invalid member object, erase.
}
} else {
// Not found.
}
}
_membersExpiringSoon.erase(next++);
}
}
void DBMirrorSet::memberWillExpire(int64_t expTime, uint64_t nwid, uint64_t memberId)
{
std::unique_lock<std::mutex> l(_membersExpiringSoon_l);
_membersExpiringSoon.insert(std::pair< int64_t, std::pair< uint64_t, uint64_t > >(expTime, std::pair< uint64_t, uint64_t >(nwid, memberId)));
}
} // namespace ZeroTier

View file

@ -52,7 +52,6 @@ public:
virtual void onNetworkMemberDeauthorize(const void *db,uint64_t networkId,uint64_t memberId);
AuthInfo getSSOAuthInfo(const nlohmann::json &member, const std::string &redirectURL);
void networkMemberSSOHasExpired(uint64_t nwid, int64_t ts);
inline void addDB(const std::shared_ptr<DB> &db)
{
@ -61,17 +60,12 @@ public:
_dbs.push_back(db);
}
void membersExpiring(std::set< std::pair<uint64_t, uint64_t> > &soon, std::set< std::pair<uint64_t, uint64_t> > &expired);
void memberWillExpire(int64_t expTime, uint64_t nwid, uint64_t memberId);
private:
DB::ChangeListener *const _listener;
std::atomic_bool _running;
std::thread _syncCheckerThread;
std::vector< std::shared_ptr< DB > > _dbs;
mutable std::mutex _dbs_l;
std::set< std::pair< int64_t, std::pair<uint64_t, uint64_t> > > _membersExpiringSoon;
mutable std::mutex _membersExpiringSoon_l;
};
} // namespace ZeroTier

View file

@ -1262,6 +1262,7 @@ void EmbeddedNetworkController::_request(
}
const bool newMember = ((!member.is_object())||(member.empty()));
DB::initMember(member);
_MemberStatusKey msk(nwid,identity.address().toInt());
{
const std::string haveIdStr(OSUtils::jsonString(member["identity"],""));
@ -1335,43 +1336,21 @@ void EmbeddedNetworkController::_request(
// Should we check SSO Stuff?
// If network is configured with SSO, and the member is not marked exempt: yes
// Otherwise no, we use standard auth logic.
AuthInfo info;
int64_t authenticationExpiryTime = -1;
bool networkSSOEnabled = OSUtils::jsonBool(network["ssoEnabled"], false);
bool memberSSOExempt = OSUtils::jsonBool(member["ssoExempt"], false);
AuthInfo info;
if (networkSSOEnabled && !memberSSOExempt) {
// TODO: Get expiry time if auth is still valid
// else get new auth info & stuff
authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0);
info = _db.getSSOAuthInfo(member, _ssoRedirectURL);
assert(info.enabled == networkSSOEnabled);
std::string memberId = member["id"];
//fprintf(stderr, "ssoEnabled && !ssoExempt %s-%s\n", nwids, memberId.c_str());
uint64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0);
fprintf(stderr, "authExpiryTime: %lld\n", authenticationExpiryTime);
if (authenticationExpiryTime < now) {
fprintf(stderr, "Handling expired member\n");
if (authenticationExpiryTime <= now) {
if (info.version == 0) {
if (!info.authenticationURL.empty()) {
_db.networkMemberSSOHasExpired(nwid, now);
onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt());
Dictionary<4096> authInfo;
authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, (uint64_t)0ULL);
authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str());
//fprintf(stderr, "sending auth URL: %s\n", authenticationURL.c_str());
DB::cleanMember(member);
_db.save(member,true);
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
return;
}
}
else if (info.version == 1) {
_db.networkMemberSSOHasExpired(nwid, now);
onNetworkMemberDeauthorize(&_db, nwid, identity.address().toInt());
Dictionary<4096> authInfo;
authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, (uint64_t)0ULL);
authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str());
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
} else if (info.version == 1) {
Dictionary<8192> authInfo;
authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version);
authInfo.add(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, info.issuerURL.c_str());
@ -1379,20 +1358,11 @@ void EmbeddedNetworkController::_request(
authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str());
authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str());
DB::cleanMember(member);
_db.save(member, true);
fprintf(stderr, "Sending NC_ERROR_AUTHENTICATION_REQUIRED\n");
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes());
return;
}
else {
fprintf(stderr, "invalid sso info.version %llu\n", info.version);
}
} else if (authorized) {
fprintf(stderr, "Setting member will expire to: %lld\n", authenticationExpiryTime);
_db.memberWillExpire(authenticationExpiryTime, nwid, identity.address().toInt());
DB::cleanMember(member);
_db.save(member,true);
return;
}
}
@ -1411,8 +1381,8 @@ void EmbeddedNetworkController::_request(
{
std::lock_guard<std::mutex> l(_memberStatus_l);
_MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())];
_MemberStatus &ms = _memberStatus[msk];
ms.authenticationExpiryTime = authenticationExpiryTime;
ms.vMajor = (int)vMajor;
ms.vMinor = (int)vMinor;
ms.vRev = (int)vRev;
@ -1420,9 +1390,13 @@ void EmbeddedNetworkController::_request(
ms.lastRequestMetaData = metaData;
ms.identity = identity;
}
}
if (authenticationExpiryTime > 0) {
std::lock_guard<std::mutex> l(_expiringSoon_l);
_expiringSoon.insert(std::pair<int64_t, _MemberStatusKey>(authenticationExpiryTime, msk));
}
}
} else {
// If they are not authorized, STOP!
DB::cleanMember(member);
_db.save(member,true);
@ -1434,18 +1408,13 @@ void EmbeddedNetworkController::_request(
// If we made it this far, they are authorized (and authenticated).
// -------------------------------------------------------------------------
int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
if (now > ns.mostRecentDeauthTime) {
// If we recently de-authorized a member, shrink credential TTL/max delta to
// be below the threshold required to exclude it. Cap this to a min/max to
// prevent jitter or absurdly large values.
const uint64_t deauthWindow = now - ns.mostRecentDeauthTime;
if (deauthWindow < ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA) {
credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA;
} else if (deauthWindow < (ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA + 5000ULL)) {
credentialtmd = deauthWindow - 5000ULL;
}
// Default timeout: 15 minutes. Maximum: two hours. Can be specified by an optional field in the network config
// if something longer than 15 minutes is desired. Minimum is 5 minutes since shorter than that would be flaky.
int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_DFL_MAX_DELTA;
if (network.contains("certificateTimeoutWindowSize")) {
credentialtmd = (int64_t)network["certificateTimeoutWindowSize"];
}
credentialtmd = std::max(std::min(credentialtmd, ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA), ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA);
std::unique_ptr<NetworkConfig> nc(new NetworkConfig());
@ -1460,7 +1429,7 @@ void EmbeddedNetworkController::_request(
nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
nc->ssoEnabled = OSUtils::jsonBool(network["ssoEnabled"], false);
nc->ssoEnabled = networkSSOEnabled; //OSUtils::jsonBool(network["ssoEnabled"], false);
nc->ssoVersion = info.version;
if (info.version == 0) {
@ -1858,6 +1827,8 @@ void EmbeddedNetworkController::_startThreads()
const long hwc = std::max((long)std::thread::hardware_concurrency(),(long)1);
for(long t=0;t<hwc;++t) {
_threads.emplace_back([this]() {
std::vector<_MemberStatusKey> expired;
nlohmann::json network, member;
for(;;) {
_RQEntry *qe = (_RQEntry *)0;
auto timedWaitResult = _queue.get(qe, 1000);
@ -1876,28 +1847,31 @@ void EmbeddedNetworkController::_startThreads()
}
}
std::set< std::pair<uint64_t, uint64_t> > soon;
std::set< std::pair<uint64_t, uint64_t> > expired;
_db.membersExpiring(soon, expired);
for(auto s=soon.begin();s!=soon.end();++s) {
Identity identity;
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> lastMetaData;
{
std::unique_lock<std::mutex> ll(_memberStatus_l);
auto ms = _memberStatus.find(_MemberStatusKey(s->first, s->second));
if (ms != _memberStatus.end()) {
lastMetaData = ms->second.lastRequestMetaData;
identity = ms->second.identity;
expired.clear();
int64_t now = OSUtils::now();
{
std::lock_guard<std::mutex> l(_expiringSoon_l);
for(auto s=_expiringSoon.begin();s!=_expiringSoon.end();) {
const int64_t when = s->first;
if (when <= now) {
// The user may have re-authorized, so we must actually look it up and check.
network.clear();
member.clear();
if (_db.get(s->second.networkId, network, s->second.nodeId, member)) {
int64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0);
if (authenticationExpiryTime <= now) {
expired.push_back(s->second);
}
}
_expiringSoon.erase(s++);
} else {
// Don't bother going further into the future than necessary.
break;
}
}
if (identity) {
request(s->first,InetAddress(),0,identity,lastMetaData);
}
}
for(auto e=expired.begin();e!=expired.end();++e) {
onNetworkMemberDeauthorize(nullptr, e->first, e->second);
onNetworkMemberDeauthorize(nullptr, e->networkId, e->nodeId);
}
}
});

View file

@ -35,7 +35,7 @@
#include "../osdep/Thread.hpp"
#include "../osdep/BlockingQueue.hpp"
#include "../ext/json/json.hpp"
#include <nlohmann/json.hpp>
#include "DB.hpp"
#include "DBMirrorSet.hpp"
@ -109,6 +109,7 @@ private:
RQENTRY_TYPE_REQUEST = 0
} type;
};
struct _MemberStatusKey
{
_MemberStatusKey() : networkId(0),nodeId(0) {}
@ -116,11 +117,13 @@ private:
uint64_t networkId;
uint64_t nodeId;
inline bool operator==(const _MemberStatusKey &k) const { return ((k.networkId == networkId)&&(k.nodeId == nodeId)); }
inline bool operator<(const _MemberStatusKey &k) const { return (k.networkId < networkId) || ((k.networkId == networkId)&&(k.nodeId < nodeId)); }
};
struct _MemberStatus
{
_MemberStatus() : lastRequestTime(0),vMajor(-1),vMinor(-1),vRev(-1),vProto(-1) {}
uint64_t lastRequestTime;
_MemberStatus() : lastRequestTime(0),authenticationExpiryTime(-1),vMajor(-1),vMinor(-1),vRev(-1),vProto(-1) {}
int64_t lastRequestTime;
int64_t authenticationExpiryTime;
int vMajor,vMinor,vRev,vProto;
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> lastRequestMetaData;
Identity identity;
@ -152,6 +155,9 @@ private:
std::unordered_map< _MemberStatusKey,_MemberStatus,_MemberStatusHash > _memberStatus;
std::mutex _memberStatus_l;
std::set< std::pair<int64_t, _MemberStatusKey> > _expiringSoon;
std::mutex _expiringSoon_l;
RedisConfig *_rc;
std::string _ssoRedirectURL;
};

View file

@ -28,7 +28,7 @@
#include <chrono>
// #define ZT_TRACE 1
// #define REDIS_TRACE 1
using json = nlohmann::json;
@ -783,6 +783,7 @@ void PostgreSQL::initializeMembers()
std::string assignedAddresses = std::get<20>(row);
config["id"] = memberId;
config["address"] = memberId;
config["nwid"] = networkId;
config["activeBridge"] = activeBridge.value_or(false);
config["authorized"] = authorized.value_or(false);
@ -942,30 +943,31 @@ void PostgreSQL::_membersWatcher_Postgres() {
void PostgreSQL::_membersWatcher_Redis() {
char buf[11] = {0};
std::string key = "member-stream:{" + std::string(_myAddress.toString(buf)) + "}";
std::string lastID = "0";
fprintf(stderr, "Listening to member stream: %s\n", key.c_str());
while (_run == 1) {
try {
json tmp;
std::unordered_map<std::string, ItemStream> result;
if (_rc->clusterMode) {
_cluster->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
_cluster->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end()));
} else {
_redis->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
_redis->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end()));
}
if (!result.empty()) {
for (auto element : result) {
#ifdef ZT_TRACE
#ifdef REDIS_TRACE
fprintf(stdout, "Received notification from: %s\n", element.first.c_str());
#endif
for (auto rec : element.second) {
std::string id = rec.first;
auto attrs = rec.second;
#ifdef ZT_TRACE
#ifdef REDIS_TRACE
fprintf(stdout, "Record ID: %s\n", id.c_str());
fprintf(stdout, "attrs len: %lu\n", attrs.size());
#endif
for (auto a : attrs) {
#ifdef ZT_TRACE
#ifdef REDIS_TRACE
fprintf(stdout, "key: %s\nvalue: %s\n", a.first.c_str(), a.second.c_str());
#endif
try {
@ -987,6 +989,7 @@ void PostgreSQL::_membersWatcher_Redis() {
} else {
_redis->xdel(key, id);
}
lastID = id;
}
}
}
@ -1029,31 +1032,31 @@ void PostgreSQL::_networksWatcher_Postgres() {
void PostgreSQL::_networksWatcher_Redis() {
char buf[11] = {0};
std::string key = "network-stream:{" + std::string(_myAddress.toString(buf)) + "}";
std::string lastID = "0";
while (_run == 1) {
try {
json tmp;
std::unordered_map<std::string, ItemStream> result;
if (_rc->clusterMode) {
_cluster->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
_cluster->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end()));
} else {
_redis->xread(key, "$", std::chrono::seconds(1), 0, std::inserter(result, result.end()));
_redis->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end()));
}
if (!result.empty()) {
for (auto element : result) {
#ifdef ZT_TRACE
#ifdef REDIS_TRACE
fprintf(stdout, "Received notification from: %s\n", element.first.c_str());
#endif
for (auto rec : element.second) {
std::string id = rec.first;
auto attrs = rec.second;
#ifdef ZT_TRACE
#ifdef REDIS_TRACE
fprintf(stdout, "Record ID: %s\n", id.c_str());
fprintf(stdout, "attrs len: %lu\n", attrs.size());
#endif
for (auto a : attrs) {
#ifdef ZT_TRACE
#ifdef REDIS_TRACE
fprintf(stdout, "key: %s\nvalue: %s\n", a.first.c_str(), a.second.c_str());
#endif
try {
@ -1075,6 +1078,7 @@ void PostgreSQL::_networksWatcher_Redis() {
} else {
_redis->xdel(key, id);
}
lastID = id;
}
}
}

24
debian/changelog vendored
View file

@ -1,3 +1,27 @@
zerotier-one (1.10.0) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 03 Jun 2022 01:00:00 -0700
zerotier-one (1.8.10) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 10 May 2022 01:00:00 -0700
zerotier-one (1.8.9) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 25 Apr 2022 01:00:00 -0700
zerotier-one (1.8.8) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 11 Apr 2022 01:00:00 -0700
zerotier-one (1.8.7) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.

2
debian/control vendored
View file

@ -10,7 +10,7 @@ Homepage: https://www.zerotier.com/
Package: zerotier-one
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, iproute2, adduser, libstdc++6, openssl
Depends: iproute2, adduser, libstdc++6 (>= 5), openssl
Homepage: https://www.zerotier.com/
Description: ZeroTier network virtualization service
ZeroTier One lets you join ZeroTier virtual networks and

View file

@ -1,4 +1,4 @@
FROM alpine:3.11.3
FROM alpine:3.15
ARG go_pkg_url

View file

@ -1,7 +1,7 @@
#!/bin/sh
grepzt() {
[ -f /var/lib/zerotier-one/zerotier-one.pid -a -n "$(cat /var/lib/zerotier-one/zerotier-one.pid)" -a -d "/proc/$(cat /var/lib/zerotier-one/zerotier-one.pid)" ]
[ -f /var/lib/zerotier-one/zerotier-one.pid -a -n "$(cat /var/lib/zerotier-one/zerotier-one.pid 2>/dev/null)" -a -d "/proc/$(cat /var/lib/zerotier-one/zerotier-one.pid 2>/dev/null)" ]
return $?
}
@ -33,29 +33,57 @@ fi
mkztfile zerotier-one.port 0600 "9993"
killzerotier() {
echo "Killing zerotier"
kill $(cat /var/lib/zerotier-one/zerotier-one.pid)
log "Killing zerotier"
kill $(cat /var/lib/zerotier-one/zerotier-one.pid 2>/dev/null)
exit 0
}
log_header() {
echo -n "\r=>"
}
log_detail_header() {
echo -n "\r===>"
}
log() {
echo "$(log_header)" "$@"
}
log_params() {
title=$1
shift
log "$title" "[$@]"
}
log_detail() {
echo "$(log_detail_header)" "$@"
}
log_detail_params() {
title=$1
shift
log_detail "$title" "[$@]"
}
trap killzerotier INT TERM
echo "Configuring networks to join"
log "Configuring networks to join"
mkdir -p /var/lib/zerotier-one/networks.d
echo "joining networks: $@"
log_params "Joining networks:" $@
for i in "$@"
do
echo "Configuring join for $i"
log_detail_params "Configuring join:" "$i"
touch "/var/lib/zerotier-one/networks.d/${i}.conf"
done
echo "starting zerotier"
log "Starting ZeroTier"
nohup /usr/sbin/zerotier-one &
while ! grepzt
do
echo "zerotier hasn't started, waiting a second"
log_detail "ZeroTier hasn't started, waiting a second"
if [ -f nohup.out ]
then
@ -65,7 +93,7 @@ do
sleep 1
done
echo "Writing healthcheck for networks: $@"
log_params "Writing healthcheck for networks:" $@
cat >/healthcheck.sh <<EOF
#!/bin/bash
@ -77,7 +105,9 @@ EOF
chmod +x /healthcheck.sh
echo "Sleeping infinitely"
log_params "zerotier-cli info:" "$(zerotier-cli info)"
log "Sleeping infinitely"
while true
do
sleep 1

21
ext/inja/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2021 Berscheid
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

391
ext/inja/README.md Normal file
View file

@ -0,0 +1,391 @@
[<div align="center"><img width="500" src="https://raw.githubusercontent.com/pantor/inja/master/doc/logo.svg?sanitize=true"></div>](https://github.com/pantor/inja/releases)
<p align="center">
<a href="https://github.com/pantor/inja/actions">
<img src="https://github.com/pantor/inja/workflows/CI/badge.svg" alt="CI Status">
</a>
<a href="https://github.com/pantor/inja/actions">
<img src="https://github.com/pantor/inja/workflows/Documentation/badge.svg" alt="Documentation Status">
</a>
<a href="https://www.codacy.com/manual/pantor/inja?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=pantor/inja&amp;utm_campaign=Badge_Grade">
<img src="https://app.codacy.com/project/badge/Grade/211718f7a36541819d1244c0e2ee6f08"/>
</a>
<a href="https://github.com/pantor/inja/releases">
<img src="https://img.shields.io/github/release/pantor/inja.svg" alt="Github Releases">
</a>
<a href="http://github.com/pantor/inja/issues">
<img src="https://img.shields.io/github/issues/pantor/inja.svg" alt="Github Issues">
</a>
<a href="https://raw.githubusercontent.com/pantor/inja/master/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="GitHub License">
</a>
</p>
Inja is a template engine for modern C++, loosely inspired by [jinja](http://jinja.pocoo.org) for python. It has an easy and yet powerful template syntax with all variables, loops, conditions, includes, callbacks, and comments you need, nested and combined as you like. Inja uses the wonderful [json](https://github.com/nlohmann/json) library by nlohmann for data input. Most importantly, inja needs only two header files, which is (nearly) as trivial as integration in C++ can get. Of course, everything is tested on all relevant compilers. Here is what it looks like:
```.cpp
json data;
data["name"] = "world";
inja::render("Hello {{ name }}!", data); // Returns "Hello world!"
```
## Integration
Inja is a headers only library, which can be downloaded from the [releases](https://github.com/pantor/inja/releases) or directly from the `include/` or `single_include/` folder. Inja uses `nlohmann/json.hpp` (>= v3.8.0) as its single dependency, so make sure it can be included from `inja.hpp`. json can be downloaded [here](https://github.com/nlohmann/json/releases). Then integration is as easy as:
```.cpp
#include <inja.hpp>
// Just for convenience
using namespace inja;
```
If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repository as a subproject.
If you are using [Conan](https://conan.io) to manage your dependencies, have a look at [this repository](https://github.com/DEGoodmanWilson/conan-inja). Please file issues [here](https://github.com/DEGoodmanWilson/conan-inja/issues) if you experience problems with the packages.
You can also integrate inja in your project using [Hunter](https://github.com/cpp-pm/hunter), a package manager for C++.
If you are using [vcpkg](https://github.com/Microsoft/vcpkg) on your project for external dependencies, then you can use the [inja package](https://github.com/Microsoft/vcpkg/tree/master/ports/inja). Please see the vcpkg project for any issues regarding the packaging.
If you are using [cget](https://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install pantor/inja`. A specific version can be installed with `cget install pantor/inja@v2.1.0`.
On macOS, you can install inja via [Homebrew](https://formulae.brew.sh/formula/inja#default) and `brew install inja`.
If you are using [conda](https://docs.conda.io/en/latest/), you can install the latest version from [conda-forge](https://anaconda.org/conda-forge/inja) with `conda install -c conda-forge inja`.
## Tutorial
This tutorial will give you an idea how to use inja. It will explain the most important concepts and give practical advices using examples and executable code. Beside this tutorial, you may check out the [documentation](https://pantor.github.io/inja).
### Template Rendering
The basic template rendering takes a template as a `std::string` and a `json` object for all data. It returns the rendered template as an `std::string`.
```.cpp
json data;
data["name"] = "world";
render("Hello {{ name }}!", data); // Returns std::string "Hello world!"
render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream
```
For more advanced usage, an environment is recommended.
```.cpp
Environment env;
// Render a string with json data
std::string result = env.render("Hello {{ name }}!", data); // "Hello world!"
// Or directly read a template file
Template temp = env.parse_template("./templates/greeting.txt");
std::string result = env.render(temp, data); // "Hello world!"
data["name"] = "Inja";
std::string result = env.render(temp, data); // "Hello Inja!"
// Or read the template file (and/or the json file) directly from the environment
result = env.render_file("./templates/greeting.txt", data);
result = env.render_file_with_json_file("./templates/greeting.txt", "./data.json");
// Or write a rendered template file
env.write(temp, data, "./result.txt");
env.write_with_json_file("./templates/greeting.txt", "./data.json", "./result.txt");
```
The environment class can be configured to your needs.
```.cpp
// With default settings
Environment env_default;
// With global path to template files and where files will be saved
Environment env_1 {"../path/templates/"};
// With separate input and output path
Environment env_2 {"../path/templates/", "../path/results/"};
// With other opening and closing strings (here the defaults)
env.set_expression("{{", "}}"); // Expressions
env.set_comment("{#", "#}"); // Comments
env.set_statement("{%", "%}"); // Statements {% %} for many things, see below
env.set_line_statement("##"); // Line statements ## (just an opener)
```
### Variables
Variables are rendered within the `{{ ... }}` expressions.
```.cpp
json data;
data["neighbour"] = "Peter";
data["guests"] = {"Jeff", "Tom", "Patrick"};
data["time"]["start"] = 16;
data["time"]["end"] = 22;
// Indexing in array
render("{{ guests.1 }}", data); // "Tom"
// Objects
render("{{ time.start }} to {{ time.end + 1 }}pm", data); // "16 to 23pm"
```
If no variable is found, valid JSON is printed directly, otherwise an `inja::RenderError` is thrown.
### Statements
Statements can be written either with the `{% ... %}` syntax or the `##` syntax for entire lines. Note that `##` needs to start the line without indentation. The most important statements are loops, conditions and file includes. All statements can be nested.
#### Loops
```.cpp
// Combining loops and line statements
render(R"(Guest List:
## for guest in guests
{{ loop.index1 }}: {{ guest }}
## endfor )", data)
/* Guest List:
1: Jeff
2: Tom
3: Patrick */
```
In a loop, the special variables `loop.index (number)`, `loop.index1 (number)`, `loop.is_first (boolean)` and `loop.is_last (boolean)` are defined. In nested loops, the parent loop variables are available e.g. via `loop.parent.index`. You can also iterate over objects like `{% for key, value in time %}`.
#### Conditions
Conditions support the typical if, else if and else statements. Following conditions are for example possible:
```.cpp
// Standard comparisons with a variable
render("{% if time.hour >= 20 %}Serve{% else if time.hour >= 18 %}Make{% endif %} dinner.", data); // Serve dinner.
// Variable in list
render("{% if neighbour in guests %}Turn up the music!{% endif %}", data); // Turn up the music!
// Logical operations
render("{% if guest_count < (3+2) and all_tired %}Sleepy...{% else %}Keep going...{% endif %}", data); // Sleepy...
// Negations
render("{% if not guest_count %}The End{% endif %}", data); // The End
```
#### Includes
You can either include other in-memory templates or from the file system.
```.cpp
// To include in-memory templates, add them to the environment first
inja::Template content_template = env.parse("Hello {{ neighbour }}!");
env.include_template("content", content_template);
env.render("Content: {% include \"content\" %}", data); // "Content: Hello Peter!"
// Other template files are included relative from the current file location
render("{% include \"footer.html\" %}", data);
```
If a corresponding template could not be found in the file system, the *include callback* is called:
```.cpp
// The callback takes the current path and the wanted include name and returns a template
env.set_include_callback([&env](const std::string& path, const std::string& template_name) {
return env.parse("Hello {{ neighbour }} from " + template_name);
});
// You can disable to search for templates in the file system via
env.set_search_included_templates_in_files(false);
```
Inja will throw an `inja::RenderError` if an included file is not found and no callback is specified. To disable this error, you can call `env.set_throw_at_missing_includes(false)`.
#### Assignments
Variables can also be defined within the template using the set statment.
```.cpp
render("{% set new_hour=23 %}{{ new_hour }}pm", data); // "23pm"
render("{% set time.start=18 %}{{ time.start }}pm", data); // using json pointers
```
Assignments only set the value within the rendering context; they do not modify the json object passed into the `render` call.
### Functions
A few functions are implemented within the inja template syntax. They can be called with
```.cpp
// Upper and lower function, for string cases
render("Hello {{ upper(neighbour) }}!", data); // "Hello PETER!"
render("Hello {{ lower(neighbour) }}!", data); // "Hello peter!"
// Range function, useful for loops
render("{% for i in range(4) %}{{ loop.index1 }}{% endfor %}", data); // "1234"
render("{% for i in range(3) %}{{ at(guests, i) }} {% endfor %}", data); // "Jeff Tom Patrick "
// Length function (please don't combine with range, use list directly...)
render("I count {{ length(guests) }} guests.", data); // "I count 3 guests."
// Get first and last element in a list
render("{{ first(guests) }} was first.", data); // "Jeff was first."
render("{{ last(guests) }} was last.", data); // "Patir was last."
// Sort a list
render("{{ sort([3,2,1]) }}", data); // "[1,2,3]"
render("{{ sort(guests) }}", data); // "[\"Jeff\", \"Patrick\", \"Tom\"]"
// Join a list with a separator
render("{{ join([1,2,3], \" + \") }}", data); // "1 + 2 + 3"
render("{{ join(guests, \", \") }}", data); // "Jeff, Patrick, Tom"
// Round numbers to a given precision
render("{{ round(3.1415, 0) }}", data); // 3
render("{{ round(3.1415, 3) }}", data); // 3.142
// Check if a value is odd, even or divisible by a number
render("{{ odd(42) }}", data); // false
render("{{ even(42) }}", data); // true
render("{{ divisibleBy(42, 7) }}", data); // true
// Maximum and minimum values from a list
render("{{ max([1, 2, 3]) }}", data); // 3
render("{{ min([-2.4, -1.2, 4.5]) }}", data); // -2.4
// Convert strings to numbers
render("{{ int(\"2\") == 2 }}", data); // true
render("{{ float(\"1.8\") > 2 }}", data); // false
// Set default values if variables are not defined
render("Hello {{ default(neighbour, \"my friend\") }}!", data); // "Hello Peter!"
render("Hello {{ default(colleague, \"my friend\") }}!", data); // "Hello my friend!"
// Access an objects value dynamically
render("{{ at(time, \"start\") }} to {{ time.end }}", data); // "16 to 22"
// Check if a key exists in an object
render("{{ exists(\"guests\") }}", data); // "true"
render("{{ exists(\"city\") }}", data); // "false"
render("{{ existsIn(time, \"start\") }}", data); // "true"
render("{{ existsIn(time, neighbour) }}", data); // "false"
// Check if a key is a specific type
render("{{ isString(neighbour) }}", data); // "true"
render("{{ isArray(guests) }}", data); // "true"
// Implemented type checks: isArray, isBoolean, isFloat, isInteger, isNumber, isObject, isString,
```
### Callbacks
You can create your own and more complex functions with callbacks. These are implemented with `std::function`, so you can for example use C++ lambdas. Inja `Arguments` are a vector of json pointers.
```.cpp
Environment env;
/*
* Callbacks are defined by its:
* - name,
* - (optional) number of arguments,
* - callback function.
*/
env.add_callback("double", 1, [](Arguments& args) {
int number = args.at(0)->get<int>(); // Adapt the index and type of the argument
return 2 * number;
});
// You can then use a callback like a regular function
env.render("{{ double(16) }}", data); // "32"
// Inja falls back to variadic callbacks if the number of expected arguments is omitted.
env.add_callback("argmax", [](Arguments& args) {
auto result = std::max_element(args.begin(), args.end(), [](const json* a, const json* b) { return *a < *b;});
return std::distance(args.begin(), result);
});
env.render("{{ argmax(4, 2, 6) }}", data); // "2"
env.render("{{ argmax(0, 2, 6, 8, 3) }}", data); // "3"
// A callback without argument can be used like a dynamic variable:
std::string greet = "Hello";
env.add_callback("double-greetings", 0, [greet](Arguments args) {
return greet + " " + greet + "!";
});
env.render("{{ double-greetings }}", data); // "Hello Hello!"
```
You can also add a void callback without return variable, e.g. for debugging:
```.cpp
env.add_void_callback("log", 1, [greet](Arguments args) {
std::cout << "logging: " << args[0] << std::endl;
});
env.render("{{ log(neighbour) }}", data); // Prints nothing to result, only to cout...
```
### Template Inheritance
Template inheritance allows you to build a base *skeleton* template that contains all the common elements and defines blocks that child templates can override. Lets show an example: The base template
```.html
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
</body>
</html>
```
contains three `blocks` that child templates can fill in. The child template
```.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my blog!
</p>
{% endblock %}
```
calls a parent template with the `extends` keyword; it should be the first element in the template. It is possible to render the contents of the parent block by calling `super()`. In the case of multiple levels of `{% extends %}`, super references may be called with an argument (e.g. `super(2)`) to skip levels in the inheritance tree.
### Whitespace Control
In the default configuration, no whitespace is removed while rendering the file. To support a more readable template style, you can configure the environment to control whitespaces before and after a statement automatically. While enabling `set_trim_blocks` removes the first newline after a statement, `set_lstrip_blocks` strips tabs and spaces from the beginning of a line to the start of a block.
```.cpp
Environment env;
env.set_trim_blocks(true);
env.set_lstrip_blocks(true);
```
With both `trim_blocks` and `lstrip_blocks` enabled, you can put statements on their own lines. Furthermore, you can also strip whitespaces for both statements and expressions by hand. If you add a minus sign (`-`) to the start or end, the whitespaces before or after that block will be removed:
```.cpp
render("Hello {{- name -}} !", data); // "Hello Inja!"
render("{% if neighbour in guests -%} I was there{% endif -%} !", data); // Renders without any whitespaces
```
Stripping behind a statement or expression also removes any newlines.
### Comments
Comments can be written with the `{# ... #}` syntax.
```.cpp
render("Hello{# Todo #}!", data); // "Hello!"
```
### Exceptions
Inja uses exceptions to handle ill-formed template input. However, exceptions can be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `INJA_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls.
## Supported compilers
Inja uses the `string_view` feature of the C++17 STL. Currently, the following compilers are tested:
- GCC 7 - 11 (and possibly later)
- Clang 5 - 12 (and possibly later)
- Microsoft Visual C++ 2017 15.0 - 2022 (and possibly later)
A list of supported compiler / os versions can be found in the [CI definition](https://github.com/pantor/inja/blob/master/.github/workflows/ci.yml).

2949
ext/inja/inja.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -701,7 +701,7 @@
<key>USE_HFS+_COMPRESSION</key>
<false/>
<key>VERSION</key>
<string>1.8.7</string>
<string>1.10.0</string>
</dict>
<key>TYPE</key>
<integer>0</integer>

View file

@ -1,12 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="19.2" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="19.5" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
<COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
<ROW Name="HiddenItems" Value="ActSyncAppComponent;CPLAppletComponent;AutorunComponent;GameUxComponent;SilverlightSlnComponent;AppXAppDetailsComponent;FixupComponent;AppXCapabilitiesComponent;AppXDependenciesComponent;AppXProductDetailsComponent;AppXVisualAssetsComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent;MsiXDiffComponent;MsixManifestEditorComponent"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
<ROW Property="AI_CURRENT_YEAR" Value="2022" ValueLocId="-"/>
<ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
<ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
<ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
<ROW Property="AI_PACKAGING_TOOL" Value="Advanced Installer 19.5 build 36301275" ValueLocId="-"/>
<ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
<ROW Property="AI_PREREQ_REPAIR_ENABLED" MultiBuildValue="ExeBuild:1"/>
<ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
<ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
<ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
@ -27,10 +32,10 @@
<ROW Property="LIMITUI" MultiBuildValue="DefaultBuild:1"/>
<ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
<ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
<ROW Property="ProductCode" Value="1033:{1CF2BF7E-ADE7-440B-A50A-F2912D8B4D0D} " Type="16"/>
<ROW Property="ProductCode" Value="1033:{5FF7375F-69D4-4CAA-800B-3EA1E587AAAE} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One"/>
<ROW Property="ProductVersion" Value="1.8.6"/>
<ROW Property="ProductVersion" Value="1.10.0"/>
<ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
<ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
@ -49,33 +54,38 @@
<ROW Property="ZTHEADLESS" Value="No"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="3"/>
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="15"/>
<ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
<ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
<ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One" DirectoryOptions="12"/>
<ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
<ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
<ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
<ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier" DirectoryOptions="12"/>
<ROW Directory="i686_1_Dir" Directory_Parent="ProgramMenuFolder" DefaultDir=".:i686"/>
<ROW Directory="i686_Dir" Directory_Parent="APPDIR" DefaultDir=".:i686" DirectoryOptions="3"/>
<ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
<ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
<ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
<ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
<ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
<ROW Directory="i686_Dir" Directory_Parent="APPDIR" DefaultDir=".:i686" DirectoryOptions="15"/>
<ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d" DirectoryOptions="12"/>
<ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier" DirectoryOptions="12"/>
<ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows" DirectoryOptions="12"/>
<ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64" DirectoryOptions="12"/>
<ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86" DirectoryOptions="12"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="A918597FE054CCCB65ABDBA0AD8F63C" ComponentId="{458A5336-4527-4409-BB9D-D074790820A6}" Directory_="APPDIR" Attributes="4" KeyPath="A918597FE054CCCB65ABDBA0AD8F63C" Options="2"/>
<ROW Component="AI_CustomARPName" ComponentId="{B098B41F-3C1C-4D4E-BB89-FF0C81A26E9C}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
<ROW Component="A918597FE054CCCB65ABDBA0AD8F63C" ComponentId="{9EBBA2D0-7170-4C6C-9B07-9405F08DC282}" Directory_="APPDIR" Attributes="4" KeyPath="A918597FE054CCCB65ABDBA0AD8F63C" Options="2"/>
<ROW Component="AI_CustomARPName" ComponentId="{B1226053-207C-4922-AF29-49542B56F5FB}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
<ROW Component="AI_DisableModify" ComponentId="{46FFA8C5-A0CB-4E05-9AD3-911D543DE8CA}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
<ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
<ROW Component="APPDIR" ComponentId="{4DD7907D-D7FE-4CD6-B1A0-B5C1625F5133}" Directory_="APPDIR" Attributes="0"/>
<ROW Component="C4FE6FD5B7C4D07B3A313E754A9A6A8" ComponentId="{7A501107-9EEE-45A6-9609-F5E1A7372448}" Directory_="APPDIR" Attributes="260" KeyPath="C4FE6FD5B7C4D07B3A313E754A9A6A8" Options="2"/>
<ROW Component="C4FE6FD5B7C4D07B3A313E754A9A6A8" ComponentId="{8F2CBC66-14B3-4DF4-8F6E-4B79B080BB12}" Directory_="APPDIR" Attributes="4" KeyPath="C4FE6FD5B7C4D07B3A313E754A9A6A8" Options="2"/>
<ROW Component="One" ComponentId="{41AB11E7-066E-414A-96F8-F051D3D3B353}" Directory_="One_Dir" Attributes="0"/>
<ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
<ROW Component="RequiredApplication" ComponentId="{92E37C98-F8B3-4A85-AE77-3D8D9A223776}" Directory_="APPDIR" Attributes="4" Condition="VersionNT &gt;= 1000" KeyPath="RequiredApplication" Options="2"/>
<ROW Component="ZeroTier" ComponentId="{8864F744-9BDF-4891-88A1-6D23D76BCCB1}" Directory_="ZeroTier_Dir" Attributes="0"/>
<ROW Component="i686" ComponentId="{6EC46014-3BFD-4017-ACBC-C4417D1D6361}" Directory_="i686_1_Dir" Attributes="0"/>
<ROW Component="i686_1" ComponentId="{60156BDC-31D7-47EE-A307-B62129607DD5}" Directory_="i686_Dir" Attributes="0"/>
<ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
<ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
<ROW Component="tapwindows" ComponentId="{3E9CBCCE-EC9D-4802-B8FD-DADB4CC532A2}" Directory_="tapwindows_Dir" Attributes="0"/>
<ROW Component="x64" ComponentId="{4DD1F90B-53F1-4390-BDF1-E6D9B39B8D80}" Directory_="x64_Dir" Attributes="0"/>
<ROW Component="x86" ComponentId="{8E83C577-3C22-49B7-82A8-369BE1F19224}" Directory_="x86_Dir" Attributes="0"/>
<ROW Component="zerotier_desktop_ui.exe" ComponentId="{61A7F53C-C6C3-418D-A652-2E4D9F8173AA}" Directory_="APPDIR" Attributes="256" Condition="ZTHEADLESS = &quot;No&quot; AND VersionNT64" KeyPath="zerotier_desktop_ui.exe"/>
<ROW Component="zerotier_desktop_ui.exe_1" ComponentId="{5CFEA823-6D17-4EAA-BBAA-810E1C89555D}" Directory_="i686_Dir" Attributes="0" Condition="ZTHEADLESS = &quot;No&quot; AND NOT VersionNT64" KeyPath="zerotier_desktop_ui.exe_1"/>
<ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
@ -84,10 +94,9 @@
<ROW Component="zttap300_x86_win10" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="A918597FE054CCCB65ABDBA0AD8F63C" Title="Visual C++ Redistributable for Visual Studio 2015-2019 x86" Description="Visual C++ Redistributable for Visual Studio 2015-2019 x86" Display="3" Level="1" Attributes="0"/>
<ROW Feature="C4FE6FD5B7C4D07B3A313E754A9A6A8" Title="Visual C++ Redistributable for Visual Studio 2015-2019 x64" Description="Visual C++ Redistributable for Visual Studio 2015-2019 x64" Display="5" Level="1" Attributes="0"/>
<ROW Feature="A918597FE054CCCB65ABDBA0AD8F63C" Title="Visual C++ Redistributable for Visual Studio 2015-2022 x86" Description="Visual C++ Redistributable for Visual Studio 2015-2022 x86" Display="5" Level="1" Attributes="0"/>
<ROW Feature="C4FE6FD5B7C4D07B3A313E754A9A6A8" Title="Visual C++ Redistributable for Visual Studio 2015-2022 x64" Description="Visual C++ Redistributable for Visual Studio 2015-2022 x64" Display="3" Level="1" Attributes="0"/>
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0"/>
<ROW Feature="RequiredApplication" Title="Microsoft Edge Webview2 Runtime" Description="Microsoft Edge Webview2 Runtime" Display="7" Level="1" Attributes="0"/>
<ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="0" Level="1" Directory_="APPDIR" Attributes="0"/>
<ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
</COMPONENT>
@ -242,9 +251,6 @@
<ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
<ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
<ROW Dialog_="ExitDialog" Control_="Finish" Event="DoAction" Argument="AI_CleanPrereq" Condition="1" Ordering="301"/>
<ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_CleanPrereq" Condition="1" Ordering="102"/>
<ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_CleanPrereq" Condition="1" Ordering="101"/>
<ROW Dialog_="Windows7Warning" Control_="Cancel" Event="SpawnDialog" Argument="CancelDlg" Condition="1" Ordering="100"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="5" Options="2"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="6" Options="2"/>
@ -256,12 +262,21 @@
<ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
<ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
<ROW Dialog_="ExitDialog" Control_="Finish" Event="DoAction" Argument="AI_ChainerScheduleReboot" Condition="Not AIEXTERNALUI" Ordering="302"/>
<ROW Dialog_="ExitDialog" Control_="Finish" Event="DoAction" Argument="AI_CleanPrereq" Condition="1" Ordering="303"/>
<ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_CleanPrereq" Condition="1" Ordering="102"/>
<ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_CleanPrereq" Condition="1" Ordering="101"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
<ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
<ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
<ROW Directory_="APPDIR" Component_="APPDIR" ManualDelete="true"/>
<ROW Directory_="i686_1_Dir" Component_="i686" ManualDelete="false"/>
<ROW Directory_="ZeroTier_Dir" Component_="ZeroTier" ManualDelete="true"/>
<ROW Directory_="One_Dir" Component_="One" ManualDelete="false"/>
<ROW Directory_="tapwindows_Dir" Component_="tapwindows" ManualDelete="false"/>
<ROW Directory_="x64_Dir" Component_="x64" ManualDelete="false"/>
<ROW Directory_="x86_Dir" Component_="x86" ManualDelete="false"/>
<ROW Directory_="i686_Dir" Component_="i686_1" ManualDelete="false"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
<ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
@ -308,6 +323,7 @@
<ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
<ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
<ROW Action="AI_ResolveLocalizedCredentials" Type="1" Source="aicustact.dll" Target="GetLocalizedCredentials"/>
<ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
<ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
<ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
@ -353,11 +369,16 @@
<ROW Feature_="ZeroTierOne" Component_="AI_DisableModify"/>
<ROW Feature_="ZeroTierOne" Component_="zerotier_desktop_ui.exe"/>
<ROW Feature_="ZeroTierOne" Component_="zerotier_desktop_ui.exe_1"/>
<ROW Feature_="A918597FE054CCCB65ABDBA0AD8F63C" Component_="A918597FE054CCCB65ABDBA0AD8F63C"/>
<ROW Feature_="C4FE6FD5B7C4D07B3A313E754A9A6A8" Component_="C4FE6FD5B7C4D07B3A313E754A9A6A8"/>
<ROW Feature_="RequiredApplication" Component_="RequiredApplication"/>
<ROW Feature_="ZeroTierOne" Component_="AI_ExePath"/>
<ROW Feature_="ZeroTierOne" Component_="i686"/>
<ROW Feature_="ZeroTierOne" Component_="ZeroTier"/>
<ROW Feature_="ZeroTierOne" Component_="One"/>
<ROW Feature_="ZeroTierOne" Component_="tapwindows"/>
<ROW Feature_="ZeroTierOne" Component_="x64"/>
<ROW Feature_="ZeroTierOne" Component_="x86"/>
<ROW Feature_="ZeroTierOne" Component_="i686_1"/>
<ROW Feature_="C4FE6FD5B7C4D07B3A313E754A9A6A8" Component_="C4FE6FD5B7C4D07B3A313E754A9A6A8"/>
<ROW Feature_="A918597FE054CCCB65ABDBA0AD8F63C" Component_="A918597FE054CCCB65ABDBA0AD8F63C"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
@ -367,12 +388,12 @@
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
<ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1502"/>
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1397"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
<ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
<ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
<ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
<ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
<ROW Action="InstallFinalize" Sequence="6602" SeqType="0" MsiKey="InstallFinalize"/>
<ROW Action="InstallFinalize" Sequence="6604" SeqType="0" MsiKey="InstallFinalize"/>
<ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1501"/>
<ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1605"/>
<ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1606"/>
@ -393,7 +414,7 @@
<ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="6594" Builds="ExeBuild"/>
<ROW Action="TerminateUIOld" Sequence="1602"/>
<ROW Action="AI_DATA_SETTER_6" Sequence="1601"/>
<ROW Action="AI_EnableDebugLog" Sequence="51"/>
<ROW Action="AI_EnableDebugLog" Sequence="52"/>
<ROW Action="AI_AppSearchEx" Sequence="101"/>
<ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
<ROW Action="AI_ExtractFiles" Sequence="1399" Builds="ExeBuild"/>
@ -401,21 +422,24 @@
<ROW Action="AI_GetArpIconPath" Sequence="1401"/>
<ROW Action="TerminateUINew" Sequence="1604"/>
<ROW Action="AI_DATA_SETTER_5" Sequence="1603"/>
<ROW Action="AI_ConfigureChainer" Condition="((UILevel = 2) OR (UILevel = 3)) AND (NOT UPGRADINGPRODUCTCODE)" Sequence="6600"/>
<ROW Action="AI_LaunchChainer" Condition="AI_PREREQ_CHAINER AND (NOT UPGRADINGPRODUCTCODE)" Sequence="6601"/>
<ROW Action="LaunchUI" Condition="( NOT Installed ) AND ( ZTHEADLESS = &quot;No&quot; )" Sequence="6605"/>
<ROW Action="AI_DETECT_MODERNWIN" Condition="(VersionNT &gt;= 603)" Sequence="54" MsiKey="AI_DETECT_MODERNWIN"/>
<ROW Action="AI_ResolveLocalizedCredentials" Sequence="51"/>
<ROW Action="AI_ConfigureChainer" Condition="((UILevel = 2) OR (UILevel = 3)) AND (NOT UPGRADINGPRODUCTCODE)" Sequence="6602"/>
<ROW Action="AI_LaunchChainer" Condition="AI_PREREQ_CHAINER AND (NOT UPGRADINGPRODUCTCODE)" Sequence="6603"/>
<ROW Action="AI_VerifyPrereq" Sequence="1101"/>
<ROW Action="LaunchUI" Condition="( NOT Installed ) AND ( ZTHEADLESS = &quot;No&quot; )" Sequence="6603"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
<ROW Action="AI_DpiContentScale" Sequence="52"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="54"/>
<ROW Action="AI_DpiContentScale" Sequence="53"/>
<ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
<ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="103"/>
<ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
<ROW Action="AI_DetectSoftware" Sequence="102"/>
<ROW Action="AI_EnableDebugLog" Sequence="51"/>
<ROW Action="AI_EnableDebugLog" Sequence="52"/>
<ROW Action="AI_AppSearchEx" Sequence="101"/>
<ROW Action="AI_ResolveLocalizedCredentials" Sequence="51"/>
<ROW Action="AI_DownloadPrereq" Sequence="1296"/>
<ROW Action="AI_ExtractPrereq" Sequence="1297"/>
<ROW Action="AI_InstallPrerequisite" Sequence="1298"/>
@ -433,6 +457,23 @@
<ROW Condition="SETUPEXEDIR OR (REMOVE=&quot;ALL&quot;)" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/>
<ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiLockPermComponent">
<ROW LockObject="ZeroTier_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="ZeroTier_Dir" Table="CreateFolder" User="[GRP_ADMINISTRATORS]" Permission="1245631" Flags="0"/>
<ROW LockObject="One_Dir" Table="CreateFolder" User="[GRP_ADMINISTRATORS]" Permission="1245631" Flags="0"/>
<ROW LockObject="One_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="networks.d_Dir" Table="CreateFolder" User="[GRP_ADMINISTRATORS]" Permission="1245631" Flags="0"/>
<ROW LockObject="networks.d_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="tapwindows_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="tapwindows_Dir" Table="CreateFolder" User="[GRP_ADMINISTRATORS]" Permission="1245631" Flags="0"/>
<ROW LockObject="x64_Dir" Table="CreateFolder" User="[GRP_ADMINISTRATORS]" Permission="1245631" Flags="0"/>
<ROW LockObject="x64_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="x86_Dir" Table="CreateFolder" User="[GRP_ADMINISTRATORS]" Permission="1245631" Flags="0"/>
<ROW LockObject="x86_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="regid.201001.com.zerotier_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="APPDIR" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
<ROW LockObject="i686_Dir" Table="CreateFolder" User="[GRP_EVERYONE]" Permission="1179817" Flags="0"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
<ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
<ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
@ -469,7 +510,6 @@
<ROW Registry="ProductVersion_1" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="\"/>
<ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
<ROW Registry="Readme" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Readme" Value="[ARPREADME]" Component_="AI_CustomARPName"/>
<ROW Registry="RequiredApplication" Root="-1" Key="Software\Caphyon\Advanced Installer\Prereqs\[ProductCode]\[ProductVersion]" Name="RequiredApplication" Value="1" Component_="RequiredApplication"/>
<ROW Registry="Software" Root="-1" Key="Software" Name="\"/>
<ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
<ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
@ -501,17 +541,14 @@
<ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.PreReqComponent">
<ROW PrereqKey="A918597FE054CCCB65ABDBA0AD8F63C" DisplayName="Visual C++ Redistributable for Visual Studio 2015-2019 x86" VersionMin="14.26" SetupFileUrl="https://download.visualstudio.microsoft.com/download/pr/d60aa805-26e9-47df-b4e3-cd6fcc392333/A06AAC66734A618AB33C1522920654DDFC44FC13CAFAA0F0AB85B199C3D51DC0/VC_redist.x86.exe" Location="1" ExactSize="14413048" WinNT64Versions="Windows XP/Vista/Windows 7/Windows 8 x64/Windows 8.1 x64/Windows 10 x64/Windows 11 x64" Operator="0" ComLine="/q /norestart" BasicUiComLine="/q /norestart" NoUiComLine="/q /norestart" Options="f" MD5="fe6eae1c34528d1ea224569dcdc35618" TargetName="Visual C++ Redistributable for Visual Studio 2015-2019" Feature="A918597FE054CCCB65ABDBA0AD8F63C"/>
<ROW PrereqKey="C4FE6FD5B7C4D07B3A313E754A9A6A8" DisplayName="Visual C++ Redistributable for Visual Studio 2015-2019 x64" VersionMin="14.26" SetupFileUrl="https://download.visualstudio.microsoft.com/download/pr/d60aa805-26e9-47df-b4e3-cd6fcc392333/7D7105C52FCD6766BEEE1AE162AA81E278686122C1E44890712326634D0B055E/VC_redist.x64.exe" Location="1" ExactSize="14974616" WinNTVersions="Windows 9x/ME/NT/2000/XP/Vista/Windows 7/Windows 8 x86/Windows 8.1 x86/Windows 10 x86" Operator="1" ComLine="/q /norestart" BasicUiComLine="/q /norestart" NoUiComLine="/q /norestart" Options="xf" MD5="264c296cc0bf00db6ba8e7bf8cc4e706" TargetName="Visual C++ Redistributable for Visual Studio 2015-2019" Feature="C4FE6FD5B7C4D07B3A313E754A9A6A8"/>
<ROW PrereqKey="RequiredApplication" DisplayName="Microsoft Edge Webview2 Runtime" SetupFileUrl="https://go.microsoft.com/fwlink/p/?LinkId=2124703" Location="1" ExactSize="0" Operator="1" ComLine="/silent /install" BasicUiComLine="/silent /install" NoUiComLine="/silent /install" Options="fx" TargetName="MicrosoftEdgeWebview2Setup.exe" Feature="RequiredApplication"/>
<ATTRIBUTE name="PrereqsOrder" value="A918597FE054CCCB65ABDBA0AD8F63C C4FE6FD5B7C4D07B3A313E754A9A6A8 RequiredApplication"/>
<ROW PrereqKey="A918597FE054CCCB65ABDBA0AD8F63C" DisplayName="Visual C++ Redistributable for Visual Studio 2015-2022 x86" VersionMin="14.30" SetupFileUrl="Prerequisites\Visual C++ Redistributable for Visual Studio 2015-2022\VC_redist.x86.exe" Location="0" ExactSize="0" WinNTVersions="Windows Vista RTM x86, Windows Vista SP1 x86, Windows Server 2008 RTM x86, Windows 7 RTM x86" WinNT64Versions="Windows Vista RTM x64, Windows Vista SP1 x64, Windows Server 2008 RTM x64, Windows 7 RTM x64, Windows Server 2008 R2 RTM x64" Operator="0" NoUiComLine="/q /norestart" Options="f" TargetName="Visual C++ Redistributable for Visual Studio 2015-2022\VC_redist.x86.exe" Feature="A918597FE054CCCB65ABDBA0AD8F63C"/>
<ROW PrereqKey="C4FE6FD5B7C4D07B3A313E754A9A6A8" DisplayName="Visual C++ Redistributable for Visual Studio 2015-2022 x64" VersionMin="14.30" SetupFileUrl="Prerequisites\Visual C++ Redistributable for Visual Studio 2015-2022\VC_redist.x64.exe" Location="0" ExactSize="0" WinNTVersions="Windows 9x/ME/NT/2000/XP/Vista/Windows 7/Windows 8 x86/Windows 8.1 x86/Windows 10 x86" WinNT64Versions="Windows Vista RTM x64, Windows Vista SP1 x64, Windows Server 2008 RTM x64, Windows 7 RTM x64, Windows Server 2008 R2 RTM x64" Operator="1" NoUiComLine="/q /norestart" Options="xf" TargetName="Visual C++ Redistributable for Visual Studio 2015-2022\VC_redist.x64.exe" Feature="C4FE6FD5B7C4D07B3A313E754A9A6A8"/>
<ATTRIBUTE name="PrereqsOrder" value="C4FE6FD5B7C4D07B3A313E754A9A6A8 A918597FE054CCCB65ABDBA0AD8F63C"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.PreReqSearchComponent">
<ROW SearchKey="A918597FE054CCCB65ABDBA0AD8F63CSyst" Prereq="A918597FE054CCCB65ABDBA0AD8F63C" SearchType="0" SearchString="[SystemFolder]vcruntime140.dll" VerMin="14.26.28720" Order="2" Property="PreReqSearch_1_A918597FE054CCCB65AB"/>
<ROW SearchKey="A918597FE054CCCB65ABDBA0AD8F63CVers" Prereq="A918597FE054CCCB65ABDBA0AD8F63C" SearchType="2" SearchString="HKLM\SOFTWARE\Microsoft\DevDiv\VC\Servicing\14.0\RuntimeMinimum\Version" VerMin="14.26.28720" Order="1" Property="PreReqSearch_A918597FE054CCCB65ABDB"/>
<ROW SearchKey="C4FE6FD5B7C4D07B3A313E754A9A6A8Vers" Prereq="C4FE6FD5B7C4D07B3A313E754A9A6A8" SearchType="2" SearchString="HKLM\SOFTWARE\Microsoft\DevDiv\VC\Servicing\14.0\RuntimeMinimum\Version" VerMin="14.26.28720" Order="1" Property="PreReqSearch_C4FE6FD5B7C4D07B3A313E" Platform="1"/>
<ROW SearchKey="F3017226FE2A42958BDF00C3A9A7E4C5" Prereq="RequiredApplication" SearchType="5" SearchString="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Order="2" Property="PreReqSearch_1" Platform="1"/>
<ROW SearchKey="SystemFolderfile.dll" Prereq="RequiredApplication" SearchType="5" SearchString="HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Order="1" Property="PreReqSearch" Platform="1"/>
<ROW SearchKey="A918597FE054CCCB65ABDBA0AD8F63CSyst" Prereq="A918597FE054CCCB65ABDBA0AD8F63C" SearchType="0" SearchString="[SystemFolder]vcruntime140.dll" VerMin="14.30.30704" Order="2" Property="PreReqSearch_1_A918597FE054CCCB65AB"/>
<ROW SearchKey="A918597FE054CCCB65ABDBA0AD8F63CVers" Prereq="A918597FE054CCCB65ABDBA0AD8F63C" SearchType="2" SearchString="HKLM\SOFTWARE\Microsoft\DevDiv\VC\Servicing\14.0\RuntimeMinimum\Version" VerMin="14.30.30704" Order="1" Property="PreReqSearch_A918597FE054CCCB65ABDB"/>
<ROW SearchKey="C4FE6FD5B7C4D07B3A313E754A9A6A8Vers" Prereq="C4FE6FD5B7C4D07B3A313E754A9A6A8" SearchType="2" SearchString="HKLM\SOFTWARE\Microsoft\DevDiv\VC\Servicing\14.0\RuntimeMinimum\Version" VerMin="14.30.30704" Order="1" Property="PreReqSearch_C4FE6FD5B7C4D07B3A313E"/>
<ROW SearchKey="UpgradeCode" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="2" Property="ZTTAP300_X86_INSTALLED"/>
<ROW SearchKey="_" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="1" Property="ZTTAP300_X64_INSTALLED"/>
</COMPONENT>
@ -536,10 +573,10 @@
<ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
<ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="6" UpdateIndexInParent="0"/>
<ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="0" UpdateIndexInParent="0"/>
<ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false" UpdateIndexInParent="0"/>
<ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1" UpdateIndexInParent="0"/>
<ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="8" UpdateIndexInParent="0"/>
<ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="10" UpdateIndexInParent="0"/>
<ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]" UpdateIndexInParent="0"/>
<ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc." UpdateIndexInParent="0"/>
<ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc." UpdateIndexInParent="0"/>

View file

@ -1079,7 +1079,8 @@ enum ZT_Architecture
ZT_ARCHITECTURE_DOTNET_CLR = 13,
ZT_ARCHITECTURE_JAVA_JVM = 14,
ZT_ARCHITECTURE_WEB = 15,
ZT_ARCHITECTURE_S390X = 16
ZT_ARCHITECTURE_S390X = 16,
ZT_ARCHITECTURE_LOONGARCH64 = 17
};
/**
@ -1404,11 +1405,6 @@ typedef struct
*/
int bondingPolicy;
/**
* The health status of the bond to this peer
*/
bool isHealthy;
/**
* The number of links that comprise the bond to this peer that are considered alive
*/

View file

@ -1,6 +1,6 @@
# This requires GNU make, which is typically "gmake" on BSD systems
INCLUDES=
INCLUDES=-isystem ext
DEFS=
LIBS=
@ -152,7 +152,7 @@ endif
override DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
CXXFLAGS+=$(CFLAGS) -std=c++11 #-D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_C99_MATH -D_GLIBCXX_USE_C99_MATH_TR1
CXXFLAGS+=$(CFLAGS) -std=c++17 #-D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_C99_MATH -D_GLIBCXX_USE_C99_MATH_TR1
all: one

View file

@ -1,15 +1,15 @@
# Automagically pick CLANG or RH/CentOS newer GCC if present
# This is only done if we have not overridden these with an environment or CLI variable
ifeq ($(origin CC),default)
CC:=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
CC:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/gcc ]; then echo /opt/rh/devtoolset-8/root/usr/bin/gcc; else echo $(CC); fi)
CC:=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
CC:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/gcc ]; then echo /opt/rh/devtoolset-8/root/usr/bin/gcc; else echo $(CC); fi)
endif
ifeq ($(origin CXX),default)
CXX:=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
CXX:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/g++ ]; then echo /opt/rh/devtoolset-8/root/usr/bin/g++; else echo $(CXX); fi)
CXX:=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
CXX:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/g++ ]; then echo /opt/rh/devtoolset-8/root/usr/bin/g++; else echo $(CXX); fi)
endif
INCLUDES?=-Izeroidc/target
INCLUDES?=-Izeroidc/target -isystem ext
DEFS?=
LDLIBS?=
DESTDIR?=
@ -31,7 +31,7 @@ ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
override DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
LDLIBS+=-lminiupnpc
else
override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING="\"Linux\"" -DMINIUPNPC_VERSION_STRING="\"2.0\"" -DUPNP_VERSION_STRING="\"UPnP/1.1\"" -DENABLE_STRNATPMPERR
ONE_OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
endif
ifeq ($(wildcard /usr/include/natpmp.h),)
@ -59,32 +59,33 @@ ifeq ($(ZT_SANITIZE),1)
endif
ifeq ($(ZT_DEBUG),1)
override CFLAGS+=-Wall -Wno-deprecated -g -O -pthread $(INCLUDES) $(DEFS)
override CXXFLAGS+=-Wall -Wno-deprecated -g -O -std=c++11 -pthread $(INCLUDES) $(DEFS)
override CXXFLAGS+=-Wall -Wno-deprecated -g -O -std=c++17 -pthread $(INCLUDES) $(DEFS)
ZT_TRACE=1
RUSTFLAGS=
# The following line enables optimization for the crypto code, since
# C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box!
node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CXXFLAGS=-Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
else
CFLAGS?=-O3 -fstack-protector -fPIE
CFLAGS?=-O3 -fstack-protector
override CFLAGS+=-Wall -Wno-deprecated -pthread $(INCLUDES) -DNDEBUG $(DEFS)
CXXFLAGS?=-O3 -fstack-protector -fPIE
override CXXFLAGS+=-Wall -Wno-deprecated -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
CXXFLAGS?=-O3 -fstack-protector
override CXXFLAGS+=-Wall -Wno-deprecated -std=c++17 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
LDFLAGS=-pie -Wl,-z,relro,-z,now
RUSTFLAGS=--release
endif
ifeq ($(ZT_QNAP), 1)
override DEFS+=-D__QNAP__
override DEFS+=-D__QNAP__
ZT_EMBEDDED=1
endif
ifeq ($(ZT_UBIQUITI), 1)
override DEFS+=-D__UBIQUITI__
override DEFS+=-D__UBIQUITI__
ZT_EMBEDDED=1
endif
ifeq ($(ZT_SYNOLOGY), 1)
override CFLAGS+=-fPIC
override CXXFLAGS+=-fPIC
override DEFS+=-D__SYNOLOGY__
ZT_EMBEDDED=1
endif
ifeq ($(ZT_DISABLE_COMPRESSION), 1)
@ -213,9 +214,9 @@ ifeq ($(CC_MACH),armv7hl)
ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),armv7ve)
ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_CRYPTO=1
ZT_ARCHITECTURE=3
override DEFS+=-DZT_NO_TYPE_PUNNING
ZT_USE_ARM32_NEON_ASM_CRYPTO=1
endif
ifeq ($(CC_MACH),arm64)
ZT_ARCHITECTURE=4
@ -249,6 +250,10 @@ endif
ifeq ($(CC_MACH),riscv64)
ZT_ARCHITECTURE=0
endif
ifeq ($(CC_MACH),loongarch64)
ZT_ARCHITECTURE=17
override DEFS+=-DZT_NO_TYPE_PUNNING
endif
# Fail if system architecture could not be determined
ifeq ($(ZT_ARCHITECTURE),999)
@ -268,10 +273,13 @@ ifeq ($(ZT_IA32),1)
endif
ifeq ($(ZT_SSO_SUPPORTED), 1)
ifeq ($(ZT_DEBUG),1)
LDLIBS+=zeroidc/target/debug/libzeroidc.a -ldl -lssl -lcrypto
else
LDLIBS+=zeroidc/target/release/libzeroidc.a -ldl -lssl -lcrypto
ifeq ($(ZT_EMBEDDED),)
override DEFS+=-DZT_SSO_SUPPORTED=1
ifeq ($(ZT_DEBUG),1)
LDLIBS+=zeroidc/target/debug/libzeroidc.a -ldl -lssl -lcrypto
else
LDLIBS+=zeroidc/target/release/libzeroidc.a -ldl -lssl -lcrypto
endif
endif
endif
@ -306,8 +314,8 @@ ifeq ($(ZT_ARCHITECTURE),3)
override CXXFLAGS+=-march=armv5t -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
ZT_USE_ARM32_NEON_ASM_CRYPTO=0
else
override CFLAGS+=-mfloat-abi=hard -march=armv6kz -marm -mfpu=vfp -mno-unaligned-access -mtp=cp15 -mcpu=arm1176jzf-s
override CXXFLAGS+=-mfloat-abi=hard -march=armv6kz -marm -mfpu=vfp -fexceptions -mno-unaligned-access -mtp=cp15 -mcpu=arm1176jzf-s
override CFLAGS+=-mfloat-abi=hard -march=armv6zk -marm -mfpu=vfp -mno-unaligned-access -mtp=cp15 -mcpu=arm1176jzf-s
override CXXFLAGS+=-mfloat-abi=hard -march=armv6zk -marm -mfpu=vfp -fexceptions -mno-unaligned-access -mtp=cp15 -mcpu=arm1176jzf-s
ZT_USE_ARM32_NEON_ASM_CRYPTO=0
endif
endif
@ -326,12 +334,20 @@ ifeq ($(ZT_USE_ARM32_NEON_ASM_CRYPTO),1)
override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
endif
# Position Independence
override CFLAGS+=-fPIC -fPIE
override CXXFLAGS+=-fPIC -fPIE
.PHONY: all
all: one
.PHONY: one
one: zerotier-one zerotier-idtool zerotier-cli
from_builder:
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
zerotier-one: $(CORE_OBJS) $(ONE_OBJS) one.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LDLIBS)
@ -344,7 +360,7 @@ zerotier-cli: zerotier-one
$(ONE_OBJS): zeroidc
libzerotiercore.a: FORCE
make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++11 -fstack-protector -fPIC" $(CORE_OBJS)
make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++17 -fstack-protector -fPIC" $(CORE_OBJS)
ar rcs libzerotiercore.a $(CORE_OBJS)
ranlib libzerotiercore.a
@ -384,9 +400,11 @@ debug: FORCE
make ZT_DEBUG=1 selftest
ifeq ($(ZT_SSO_SUPPORTED), 1)
ifeq ($(ZT_EMBEDDED),)
zeroidc: FORCE
# export PATH=/root/.cargo/bin:$$PATH; cd zeroidc && cargo build -j1 $(RUSTFLAGS)
export PATH=/root/.cargo/bin:$$PATH; cd zeroidc && cargo build $(RUSTFLAGS)
export PATH=/${HOME}/.cargo/bin:$$PATH; cd zeroidc && cargo build $(RUSTFLAGS)
endif
else
zeroidc:
endif
@ -440,6 +458,20 @@ uninstall: FORCE
# These are just for convenience for building Linux packages
echo_flags:
@echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~"
@echo "echo_flags :: CC=$(CC)"
@echo "echo_flags :: CXX=$(CXX)"
@echo "echo_flags :: CFLAGS=$(CFLAGS)"
@echo "echo_flags :: CXXFLAGS=$(CXXFLAGS)"
@echo "echo_flags :: LDFLAGS=$(LDFLAGS)"
@echo "echo_flags :: RUSTFLAGS=$(RUSTFLAGS)"
@echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~"
# debian: echo_flags
# @echo "building deb package"
# debuild --no-lintian -b -uc -us
debian: FORCE
debuild --no-lintian -I -i -us -uc -nc -b
@ -478,4 +510,20 @@ snap-upload-stable: FORCE
snapcraft upload --release=stable $${SNAPFILE};\
done
synology-pkg: FORCE
cd pkg/synology ; ./build.sh build
synology-docker: FORCE
cd pkg/synology/dsm7-docker/; ./build.sh build-and-push
munge_rpm:
@:$(call check_defined, VERSION)
@echo "Updating rpm spec to $(VERSION)"
ci/scripts/munge_rpm_spec.sh zerotier-one.spec $(VERSION) "Adam Ierymenko <adam.ierymenko@zerotier.com>" "see https://github.com/zerotier/ZeroTierOne for release notes"
munge_deb:
@:$(call check_defined, VERSION)
@echo "Updating debian/changelog to $(VERSION)"
ci/scripts/munge_debian_changelog.sh debian/changelog $(VERSION) "Adam Ierymenko <adam.ierymenko@zerotier.com>" "see https://github.com/zerotier/ZeroTierOne for release notes"
FORCE:

View file

@ -2,7 +2,7 @@ CC=clang
CXX=clang++
TOPDIR=$(shell PWD)
INCLUDES=-I$(shell PWD)/zeroidc/target
INCLUDES=-I$(shell PWD)/zeroidc/target -isystem $(TOPDIR)/ext
DEFS=
LIBS=
ARCH_FLAGS=-arch x86_64 -arch arm64
@ -47,10 +47,10 @@ endif
# Use fast ASM Salsa20/12 for x64 processors
DEFS+=-DZT_USE_X64_ASM_SALSA2012
CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o
CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++
CXXFLAGS=$(CFLAGS) -std=c++17 -stdlib=libc++
# Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources
DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
DEFS+=-DMACOSX -DZT_SSO_SUPPORTED -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
ONE_OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
ifeq ($(ZT_CONTROLLER),1)
MACOS_VERSION_MIN=10.15
@ -154,6 +154,10 @@ selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
zerotier-selftest: selftest
# Make compile_commands.json for clangd editor extensions. Probably works on Linux too.
compile_commands: FORCE
compiledb make ZT_DEBUG=1
# Requires Packages: http://s.sudre.free.fr/Software/Packages/about.html
mac-dist-pkg: FORCE
packagesbuild "ext/installfiles/mac/ZeroTier One.pkgproj"

View file

@ -12,11 +12,12 @@
/****/
#include "Bond.hpp"
#include "Switch.hpp"
#include <cmath>
#include <string>
#include <cstdio>
#include <string>
namespace ZeroTier {
@ -39,6 +40,9 @@ std::map<std::string, std::map<std::string, SharedPtr<Link> > > Bond::_interface
bool Bond::linkAllowed(std::string& policyAlias, SharedPtr<Link> link)
{
if (! link) {
return false;
}
bool foundInDefinitions = false;
if (_linkDefinitions.count(policyAlias)) {
auto it = _linkDefinitions[policyAlias].begin();
@ -90,7 +94,7 @@ SharedPtr<Bond> Bond::getBondByPeerId(int64_t identity)
return _bonds.count(identity) ? _bonds[identity] : SharedPtr<Bond>();
}
SharedPtr<Bond> Bond::createTransportTriggeredBond(const RuntimeEnvironment* renv, const SharedPtr<Peer>& peer)
SharedPtr<Bond> Bond::createBond(const RuntimeEnvironment* renv, const SharedPtr<Peer>& peer)
{
Mutex::Lock _l(_bonds_m);
int64_t identity = peer->identity().address().toInt();
@ -145,7 +149,13 @@ SharedPtr<Bond> Bond::createTransportTriggeredBond(const RuntimeEnvironment* ren
return SharedPtr<Bond>();
}
SharedPtr<Link> Bond::getLinkBySocket(const std::string& policyAlias, uint64_t localSocket)
void Bond::destroyBond(uint64_t peerId)
{
Mutex::Lock _l(_bonds_m);
_bonds.erase(peerId);
}
SharedPtr<Link> Bond::getLinkBySocket(const std::string& policyAlias, uint64_t localSocket, bool createIfNeeded = false)
{
Mutex::Lock _l(_links_m);
char ifname[64] = { 0 };
@ -153,10 +163,14 @@ SharedPtr<Link> Bond::getLinkBySocket(const std::string& policyAlias, uint64_t l
std::string ifnameStr(ifname);
auto search = _interfaceToLinkMap[policyAlias].find(ifnameStr);
if (search == _interfaceToLinkMap[policyAlias].end()) {
// If the link wasn't already known, add a new entry
SharedPtr<Link> s = new Link(ifnameStr, 0, 0, true, ZT_BOND_SLAVE_MODE_SPARE, "", 0.0);
_interfaceToLinkMap[policyAlias].insert(std::pair<std::string, SharedPtr<Link> >(ifnameStr, s));
return s;
if (createIfNeeded) {
SharedPtr<Link> s = new Link(ifnameStr, 0, 0, true, ZT_BOND_SLAVE_MODE_SPARE, "", 0.0);
_interfaceToLinkMap[policyAlias].insert(std::pair<std::string, SharedPtr<Link> >(ifnameStr, s));
return s;
}
else {
return SharedPtr<Link>();
}
}
else {
return search->second;
@ -218,10 +232,12 @@ Bond::Bond(const RuntimeEnvironment* renv, SharedPtr<Bond> originalBond, const S
void Bond::nominatePathToBond(const SharedPtr<Path>& path, int64_t now)
{
Mutex::Lock _l(_paths_m);
debug("attempting to nominate link %s", pathToStr(path).c_str());
/**
* Ensure the link is allowed and the path is not already present
*/
if (! RR->bc->linkAllowed(_policyAlias, getLink(path))) {
if (! RR->bc->linkAllowed(_policyAlias, getLinkBySocket(_policyAlias, path->localSocket(), true))) {
debug("link %s is not permitted according to user-specified rules", pathToStr(path).c_str());
return;
}
bool alreadyPresent = false;
@ -229,6 +245,7 @@ void Bond::nominatePathToBond(const SharedPtr<Path>& path, int64_t now)
// Sanity check
if (path.ptr() == _paths[i].p.ptr()) {
alreadyPresent = true;
debug("link %s already exists", pathToStr(path).c_str());
break;
}
}
@ -247,20 +264,22 @@ void Bond::nominatePathToBond(const SharedPtr<Path>& path, int64_t now)
// Determine if there are any other paths on this link
bool bFoundCommonLink = false;
SharedPtr<Link> commonLink = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
for (unsigned int j = 0; j < ZT_MAX_PEER_NETWORK_PATHS; ++j) {
if (_paths[j].p && _paths[j].p.ptr() != _paths[i].p.ptr()) {
if (RR->bc->getLinkBySocket(_policyAlias, _paths[j].p->localSocket()) == commonLink) {
bFoundCommonLink = true;
_paths[j].onlyPathOnLink = false;
if (commonLink) {
for (unsigned int j = 0; j < ZT_MAX_PEER_NETWORK_PATHS; ++j) {
if (_paths[j].p && _paths[j].p.ptr() != _paths[i].p.ptr()) {
if (RR->bc->getLinkBySocket(_policyAlias, _paths[j].p->localSocket()) == commonLink, true) {
bFoundCommonLink = true;
_paths[j].onlyPathOnLink = false;
}
}
}
_paths[i].ipvPref = sl->ipvPref();
_paths[i].mode = sl->mode();
_paths[i].enabled = sl->enabled();
_paths[i].onlyPathOnLink = ! bFoundCommonLink;
}
_paths[i].ipvPref = sl->ipvPref();
_paths[i].mode = sl->mode();
_paths[i].enabled = sl->enabled();
_paths[i].onlyPathOnLink = ! bFoundCommonLink;
}
log("nominate link %s", pathToStr(path).c_str());
log("nominated link %s", pathToStr(path).c_str());
break;
}
}
@ -313,7 +332,7 @@ SharedPtr<Path> Bond::getAppropriatePath(int64_t now, int32_t flowId)
}
// Reset striping counter
_rrPacketsSentOnCurrLink = 0;
if (_numBondedPaths == 1 || _rrIdx >= (ZT_MAX_PEER_NETWORK_PATHS-1)) {
if (_numBondedPaths == 1 || _rrIdx >= (ZT_MAX_PEER_NETWORK_PATHS - 1)) {
_rrIdx = 0;
}
else {
@ -367,7 +386,7 @@ void Bond::recordIncomingInvalidPacket(const SharedPtr<Path>& path)
Mutex::Lock _l(_paths_m);
for (int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (_paths[i].p == path) {
_paths[i].packetValiditySamples.push(false);
//_paths[i].packetValiditySamples.push(false);
}
}
}
@ -388,9 +407,10 @@ void Bond::recordOutgoingPacket(const SharedPtr<Path>& path, uint64_t packetId,
_lastFrame = now;
}
if (shouldRecord) {
//_paths[pathIdx].expectingAckAsOf = now;
//_paths[pathIdx].totalBytesSentSinceLastAckRecieved += payloadLength;
//_paths[pathIdx].unackedBytes += payloadLength;
// Take note that we're expecting a VERB_ACK on this path as of a specific time
if (_paths[pathIdx].qosStatsOut.size() < ZT_QOS_MAX_OUTSTANDING_RECORDS) {
if (_paths[pathIdx].qosStatsOut.size() < ZT_QOS_MAX_PENDING_RECORDS) {
_paths[pathIdx].qosStatsOut[packetId] = now;
}
}
@ -423,9 +443,25 @@ void Bond::recordIncomingPacket(const SharedPtr<Path>& path, uint64_t packetId,
_lastFrame = now;
}
if (shouldRecord) {
_paths[pathIdx].qosStatsIn[packetId] = now;
++(_paths[pathIdx].packetsReceivedSinceLastQoS);
_paths[pathIdx].packetValiditySamples.push(true);
if (_paths[pathIdx].qosStatsIn.size() < ZT_QOS_MAX_PENDING_RECORDS) {
// debug("Recording QoS information (table size = %d)", _paths[pathIdx].qosStatsIn.size());
_paths[pathIdx].qosStatsIn[packetId] = now;
++(_paths[pathIdx].packetsReceivedSinceLastQoS);
//_paths[pathIdx].packetValiditySamples.push(true);
}
else {
debug("QoS buffer full, will not record information");
}
/*
if (_paths[pathIdx].ackStatsIn.size() < ZT_ACK_MAX_PENDING_RECORDS) {
//debug("Recording ACK information (table size = %d)", _paths[pathIdx].ackStatsIn.size());
_paths[pathIdx].ackStatsIn[packetId] = payloadLength;
++(_paths[pathIdx].packetsReceivedSinceLastAck);
}
else {
debug("ACK buffer full, will not record information");
}
*/
}
}
}
@ -470,6 +506,16 @@ void Bond::receivedQoS(const SharedPtr<Path>& path, int64_t now, int count, uint
_paths[pathIdx].qosRecordSize.push(count);
}
void Bond::receivedAck(int pathIdx, int64_t now, int32_t ackedBytes)
{
/*
Mutex::Lock _l(_paths_m);
debug("received ACK of %d bytes on path %s, there are still %d un-acked bytes", ackedBytes, pathToStr(_paths[pathIdx].p).c_str(), _paths[pathIdx].unackedBytes);
_paths[pathIdx].lastAckReceived = now;
_paths[pathIdx].unackedBytes = (ackedBytes > _paths[pathIdx].unackedBytes) ? 0 : _paths[pathIdx].unackedBytes - ackedBytes;
*/
}
int32_t Bond::generateQoSPacket(int pathIdx, int64_t now, char* qosBuffer)
{
int32_t len = 0;
@ -544,7 +590,6 @@ bool Bond::assignFlowToBondedPath(SharedPtr<Flow>& flow, int64_t now)
}
flow->assignPath(_abPathIdx, now);
}
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[flow->assignedPath].p->localSocket());
debug("assign out-flow %04x to link %s (%lu / %lu flows)", flow->id, pathToStr(_paths[flow->assignedPath].p).c_str(), _paths[flow->assignedPath].assignedFlowCount, (unsigned long)_flows.size());
return true;
}
@ -631,22 +676,24 @@ void Bond::processIncomingPathNegotiationRequest(uint64_t now, SharedPtr<Path>&
return;
}
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[pathIdx].p->localSocket());
if (remoteUtility > _localUtility) {
_paths[pathIdx].p->address().toString(pathStr);
debug("peer suggests alternate link %s/%s, remote utility (%d) greater than local utility (%d), switching to suggested link\n", link->ifname().c_str(), pathStr, remoteUtility, _localUtility);
_negotiatedPathIdx = pathIdx;
}
if (remoteUtility < _localUtility) {
debug("peer suggests alternate link %s/%s, remote utility (%d) less than local utility (%d), not switching\n", link->ifname().c_str(), pathStr, remoteUtility, _localUtility);
}
if (remoteUtility == _localUtility) {
debug("peer suggests alternate link %s/%s, remote utility (%d) equal to local utility (%d)\n", link->ifname().c_str(), pathStr, remoteUtility, _localUtility);
if (_peer->_id.address().toInt() > RR->node->identity().address().toInt()) {
debug("agree with peer to use alternate link %s/%s\n", link->ifname().c_str(), pathStr);
if (link) {
if (remoteUtility > _localUtility) {
_paths[pathIdx].p->address().toString(pathStr);
debug("peer suggests alternate link %s/%s, remote utility (%d) greater than local utility (%d), switching to suggested link\n", link->ifname().c_str(), pathStr, remoteUtility, _localUtility);
_negotiatedPathIdx = pathIdx;
}
else {
debug("ignore petition from peer to use alternate link %s/%s\n", link->ifname().c_str(), pathStr);
if (remoteUtility < _localUtility) {
debug("peer suggests alternate link %s/%s, remote utility (%d) less than local utility (%d), not switching\n", link->ifname().c_str(), pathStr, remoteUtility, _localUtility);
}
if (remoteUtility == _localUtility) {
debug("peer suggests alternate link %s/%s, remote utility (%d) equal to local utility (%d)\n", link->ifname().c_str(), pathStr, remoteUtility, _localUtility);
if (_peer->_id.address().toInt() > RR->node->identity().address().toInt()) {
debug("agree with peer to use alternate link %s/%s\n", link->ifname().c_str(), pathStr);
_negotiatedPathIdx = pathIdx;
}
else {
debug("ignore petition from peer to use alternate link %s/%s\n", link->ifname().c_str(), pathStr);
}
}
}
}
@ -717,16 +764,42 @@ void Bond::sendPATH_NEGOTIATION_REQUEST(void* tPtr, int pathIdx)
if (_paths[pathIdx].p->address()) {
outp.armor(_peer->key(), false, _peer->aesKeysIfSupported());
RR->node->putPacket(tPtr, _paths[pathIdx].p->localSocket(), _paths[pathIdx].p->address(), outp.data(), outp.size());
_overheadBytes += outp.size();
}
}
void Bond::sendACK(void* tPtr, int pathIdx, int64_t localSocket, const InetAddress& atAddress, int64_t now)
{
/*
Packet outp(_peer->_id.address(), RR->identity.address(), Packet::VERB_ACK);
int32_t bytesToAck = 0;
std::map<uint64_t, uint64_t>::iterator it = _paths[pathIdx].ackStatsIn.begin();
while (it != _paths[pathIdx].ackStatsIn.end()) {
bytesToAck += it->second;
++it;
}
debug("sending ACK of %d bytes on path %s (table size = %d)", bytesToAck, pathToStr(_paths[pathIdx].p).c_str(), _paths[pathIdx].ackStatsIn.size());
outp.append<uint32_t>(bytesToAck);
if (atAddress) {
outp.armor(_peer->key(), false, _peer->aesKeysIfSupported());
RR->node->putPacket(tPtr, localSocket, atAddress, outp.data(), outp.size());
}
else {
RR->sw->send(tPtr, outp, false);
}
_paths[pathIdx].ackStatsIn.clear();
_paths[pathIdx].packetsReceivedSinceLastAck = 0;
_paths[pathIdx].lastAckSent = now;
*/
}
void Bond::sendQOS_MEASUREMENT(void* tPtr, int pathIdx, int64_t localSocket, const InetAddress& atAddress, int64_t now)
{
int64_t _now = RR->node->now();
Packet outp(_peer->_id.address(), RR->identity.address(), Packet::VERB_QOS_MEASUREMENT);
char qosData[ZT_QOS_MAX_PACKET_SIZE];
int16_t len = generateQoSPacket(pathIdx, _now, qosData);
_overheadBytes += len;
// debug("sending QOS via link %s (len=%d)", pathToStr(_paths[pathIdx].p).c_str(), len);
if (len) {
outp.append(qosData, len);
if (atAddress) {
@ -738,8 +811,8 @@ void Bond::sendQOS_MEASUREMENT(void* tPtr, int pathIdx, int64_t localSocket, con
}
_paths[pathIdx].packetsReceivedSinceLastQoS = 0;
_paths[pathIdx].lastQoSMeasurement = now;
_overheadBytes += outp.size();
}
// debug("send QOS via link %s (len=%d)", pathToStr(_paths[pathIdx].p).c_str(), len);
}
void Bond::processBackgroundBondTasks(void* tPtr, int64_t now)
@ -761,9 +834,9 @@ void Bond::processBackgroundBondTasks(void* tPtr, int64_t now)
for (unsigned int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (_paths[i].p && _paths[i].allowed()) {
if (_isLeaf) {
if ((_monitorInterval > 0) && (((now - _paths[i].p->_lastIn) >= _monitorInterval) /*|| ((now - _paths[i].p->_lastOut) >= _monitorInterval)*/)) {
if ((_monitorInterval > 0) && (((now - _paths[i].p->_lastIn) >= (_paths[i].alive ? _monitorInterval : _failoverInterval)))) {
if ((_peer->remoteVersionProtocol() >= 5) && (! ((_peer->remoteVersionMajor() == 1) && (_peer->remoteVersionMinor() == 1) && (_peer->remoteVersionRevision() == 0)))) {
Packet outp(_peer->address(), RR->identity.address(), Packet::VERB_ECHO); // ECHO (this is our bond's heartbeat)
Packet outp(_peer->address(), RR->identity.address(), Packet::VERB_ECHO); // ECHO (this is our bond's heartbeat)
outp.armor(_peer->key(), true, _peer->aesKeysIfSupported());
RR->node->expectReplyTo(outp.packetId());
RR->node->putPacket(tPtr, _paths[i].p->localSocket(), _paths[i].p->address(), outp.data(), outp.size());
@ -776,6 +849,12 @@ void Bond::processBackgroundBondTasks(void* tPtr, int64_t now)
if (_paths[i].needsToSendQoS(now, _qosSendInterval)) {
sendQOS_MEASUREMENT(tPtr, i, _paths[i].p->localSocket(), _paths[i].p->address(), now);
}
// ACK
/*
if (_paths[i].needsToSendAck(now, _ackSendInterval)) {
sendACK(tPtr, i, _paths[i].p->localSocket(), _paths[i].p->address(), now);
}
*/
}
}
}
@ -815,6 +894,24 @@ void Bond::curateBond(int64_t now, bool rebuildBond)
if (! _paths[i].p) {
continue;
}
/**
* Remove expired or invalid links from bond
*/
SharedPtr<Link> link = getLink(_paths[i].p);
if (! link) {
log("link is no longer valid, removing from bond");
_paths[i] = NominatedPath();
_paths[i].p = SharedPtr<Path>();
continue;
}
if ((now - _paths[i].p->_lastIn) > (ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD)) {
log("link (%s) has expired or is invalid, removing from bond", pathToStr(_paths[i].p).c_str());
_paths[i] = NominatedPath();
_paths[i].p = SharedPtr<Path>();
continue;
}
tmpNumTotalLinks++;
if (_paths[i].eligible) {
tmpNumAliveLinks++;
@ -875,42 +972,18 @@ void Bond::curateBond(int64_t now, bool rebuildBond)
}
/**
* Determine health status to report to user
* Trigger status report if number of links change
*/
_numAliveLinks = tmpNumAliveLinks;
_numTotalLinks = tmpNumTotalLinks;
bool tmpHealthStatus = true;
if (_policy == ZT_BOND_POLICY_BROADCAST) {
if (_numAliveLinks < 1) {
// Considered healthy if we're able to send frames at all
tmpHealthStatus = false;
}
}
if ((_policy == ZT_BOND_POLICY_BALANCE_RR) || (_policy == ZT_BOND_POLICY_BALANCE_XOR) || (_policy == ZT_BOND_POLICY_BALANCE_AWARE || (_policy == ZT_BOND_POLICY_ACTIVE_BACKUP))) {
if (_numAliveLinks < _numTotalLinks) {
tmpHealthStatus = false;
}
}
if (tmpHealthStatus != _isHealthy) {
std::string healthStatusStr;
if (tmpHealthStatus == true) {
healthStatusStr = "HEALTHY";
}
else {
healthStatusStr = "DEGRADED";
}
log("bond is %s (%d/%d links)", healthStatusStr.c_str(), _numAliveLinks, _numTotalLinks);
if ((_numAliveLinks != tmpNumAliveLinks) || (_numTotalLinks != tmpNumTotalLinks)) {
dumpInfo(now, true);
}
_isHealthy = tmpHealthStatus;
/**
* Curate the set of paths that are part of the bond proper. Select a set of paths
* per logical link according to eligibility and user-specified constraints.
*/
if ((_policy == ZT_BOND_POLICY_BALANCE_RR) || (_policy == ZT_BOND_POLICY_BALANCE_XOR) || (_policy == ZT_BOND_POLICY_BALANCE_AWARE)) {
if (! _numBondedPaths) {
rebuildBond = true;
@ -923,7 +996,9 @@ void Bond::curateBond(int64_t now, bool rebuildBond)
for (int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (_paths[i].p) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
linkMap[link].push_back(i);
if (link) {
linkMap[link].push_back(i);
}
}
}
// Re-form bond from link<->path map
@ -1008,15 +1083,19 @@ void Bond::estimatePathQuality(int64_t now)
uint32_t totUserSpecifiedLinkSpeed = 0;
if (_numBondedPaths) { // Compute relative user-specified speeds of links
for (unsigned int i = 0; i < _numBondedPaths; ++i) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
if (_paths[i].p && _paths[i].allowed()) {
totUserSpecifiedLinkSpeed += link->speed();
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
if (link) {
totUserSpecifiedLinkSpeed += link->speed();
}
}
}
for (unsigned int i = 0; i < _numBondedPaths; ++i) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
if (_paths[i].p && _paths[i].allowed()) {
link->setRelativeSpeed((uint8_t)round(((float)link->speed() / (float)totUserSpecifiedLinkSpeed) * 255));
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
if (link) {
link->setRelativeSpeed((uint8_t)round(((float)link->speed() / (float)totUserSpecifiedLinkSpeed) * 255));
}
}
}
}
@ -1044,7 +1123,7 @@ void Bond::estimatePathQuality(int64_t now)
// Compute/Smooth average of real-world observations
_paths[i].latencyMean = _paths[i].latencySamples.mean();
_paths[i].latencyVariance = _paths[i].latencySamples.stddev();
_paths[i].packetErrorRatio = 1.0 - (_paths[i].packetValiditySamples.count() ? _paths[i].packetValiditySamples.mean() : 1.0);
//_paths[i].packetErrorRatio = 1.0 - (_paths[i].packetValiditySamples.count() ? _paths[i].packetValiditySamples.mean() : 1.0);
if (userHasSpecifiedLinkSpeeds()) {
// Use user-reported metrics
@ -1054,20 +1133,52 @@ void Bond::estimatePathQuality(int64_t now)
_paths[i].throughputVariance = 0;
}
}
// Drain unacknowledged QoS records
int qosRecordTimeout = (_qosSendInterval * 3);
std::map<uint64_t, uint64_t>::iterator it = _paths[i].qosStatsOut.begin();
uint64_t currentLostRecords = 0;
int numDroppedQosOutRecords = 0;
while (it != _paths[i].qosStatsOut.end()) {
int qosRecordTimeout = 5000; //_paths[i].p->monitorInterval() * ZT_BOND_QOS_ACK_INTERVAL_MULTIPLIER * 8;
if ((now - it->second) >= qosRecordTimeout) {
// Packet was lost
it = _paths[i].qosStatsOut.erase(it);
++currentLostRecords;
++numDroppedQosOutRecords;
}
else {
++it;
}
}
if (numDroppedQosOutRecords) {
log("Dropped %d QOS out-records", numDroppedQosOutRecords);
}
/*
for (unsigned int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (! _paths[i].p) {
continue;
}
// if ((now - _paths[i].lastAckReceived) > ackSendInterval) {
// debug("been a while since ACK");
// if (_paths[i].unackedBytes > 0) {
// _paths[i].unackedBytes / _paths[i].bytesSen
// }
// }
}
*/
it = _paths[i].qosStatsIn.begin();
int numDroppedQosInRecords = 0;
while (it != _paths[i].qosStatsIn.end()) {
if ((now - it->second) >= qosRecordTimeout) {
it = _paths[i].qosStatsIn.erase(it);
++numDroppedQosInRecords;
}
else {
++it;
}
}
if (numDroppedQosInRecords) {
log("Dropped %d QOS in-records", numDroppedQosInRecords);
}
quality[i] = 0;
totQuality = 0;
@ -1197,6 +1308,7 @@ void Bond::dequeueNextActiveBackupPath(uint64_t now)
bool Bond::abForciblyRotateLink()
{
Mutex::Lock _l(_paths_m);
if (_policy == ZT_BOND_POLICY_ACTIVE_BACKUP) {
int prevPathIdx = _abPathIdx;
dequeueNextActiveBackupPath(RR->node->now());
@ -1209,9 +1321,14 @@ bool Bond::abForciblyRotateLink()
void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
{
int prevActiveBackupPathIdx = _abPathIdx;
int nonPreferredPathIdx;
int nonPreferredPathIdx = ZT_MAX_PEER_NETWORK_PATHS;
bool bFoundPrimaryLink = false;
if (_abPathIdx != ZT_MAX_PEER_NETWORK_PATHS && ! _paths[_abPathIdx].p) {
_abPathIdx = ZT_MAX_PEER_NETWORK_PATHS;
log("main active-backup path has been removed");
}
/**
* Generate periodic status report
*/
@ -1227,7 +1344,6 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
log("failover queue is empty, bond is no longer fault-tolerant");
}
}
/**
* Select initial "active" active-backup link
*/
@ -1241,7 +1357,6 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
* simply find the next eligible path.
*/
if (! userHasSpecifiedLinks()) {
debug("no user-specified links");
for (int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (_paths[i].p && _paths[i].eligible) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
@ -1253,6 +1368,7 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
}
}
}
/**
* [Manual mode]
* The user has specified links or failover rules that the bonding policy should adhere to.
@ -1264,34 +1380,38 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
continue;
}
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
if (_paths[i].eligible && link->primary()) {
if (! _paths[i].preferred()) {
// Found path on primary link, take note in case we don't find a preferred path
nonPreferredPathIdx = i;
bFoundPrimaryLink = true;
}
if (_paths[i].preferred()) {
_abPathIdx = i;
bFoundPrimaryLink = true;
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[_abPathIdx].p->localSocket());
if (link) {
log("found preferred primary link %s", pathToStr(_paths[_abPathIdx].p).c_str());
if (link) {
if (_paths[i].eligible && link->primary()) {
if (! _paths[i].preferred()) {
// Found path on primary link, take note in case we don't find a preferred path
nonPreferredPathIdx = i;
bFoundPrimaryLink = true;
}
if (_paths[i].preferred()) {
_abPathIdx = i;
bFoundPrimaryLink = true;
if (_paths[_abPathIdx].p) {
SharedPtr<Link> abLink = RR->bc->getLinkBySocket(_policyAlias, _paths[_abPathIdx].p->localSocket());
if (abLink) {
log("found preferred primary link %s", pathToStr(_paths[_abPathIdx].p).c_str());
}
break; // Found preferred path on primary link
}
}
break; // Found preferred path on primary link
}
}
}
if (bFoundPrimaryLink && nonPreferredPathIdx) {
if (bFoundPrimaryLink && (nonPreferredPathIdx != ZT_MAX_PEER_NETWORK_PATHS)) {
log("found non-preferred primary link");
_abPathIdx = nonPreferredPathIdx;
}
if (_abPathIdx == ZT_MAX_PEER_NETWORK_PATHS) {
log("user-designated primary link is not yet ready");
log("user-designated primary link is not available");
// TODO: Should wait for some time (failover interval?) and then switch to spare link
}
}
else if (! userHasSpecifiedPrimaryLink()) {
log("user did not specify a primary link, select first available link");
for (int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (_paths[i].p && _paths[i].eligible) {
_abPathIdx = i;
@ -1299,28 +1419,36 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
}
}
if (_abPathIdx != ZT_MAX_PEER_NETWORK_PATHS) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[_abPathIdx].p->localSocket());
if (link) {
log("select non-primary link %s", pathToStr(_paths[_abPathIdx].p).c_str());
if (_paths[_abPathIdx].p) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[_abPathIdx].p->localSocket());
if (link) {
log("select non-primary link %s", pathToStr(_paths[_abPathIdx].p).c_str());
}
}
}
}
}
}
// Short-circuit if we don't have an active link yet
if (_abPathIdx == ZT_MAX_PEER_NETWORK_PATHS) {
// Short-circuit if we don't have an active link yet. Everything below is optimization from the base case
if (_abPathIdx < 0 || _abPathIdx == ZT_MAX_PEER_NETWORK_PATHS || (! _paths[_abPathIdx].p)) {
return;
}
// Remove ineligible paths from the failover link queue
for (std::deque<int>::iterator it(_abFailoverQueue.begin()); it != _abFailoverQueue.end();) {
if (! _paths[(*it)].p) {
log("link is no longer valid, removing from failover queue (%zu links remain in queue)", _abFailoverQueue.size());
it = _abFailoverQueue.erase(it);
continue;
}
if (_paths[(*it)].p && ! _paths[(*it)].eligible) {
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[(*it)].p->localSocket());
it = _abFailoverQueue.erase(it);
if (link) {
log("link %s is ineligible, removing from failover queue (%zu links in queue)", pathToStr(_paths[_abPathIdx].p).c_str(), _abFailoverQueue.size());
log("link %s is ineligible, removing from failover queue (%zu links remain in queue)", pathToStr(_paths[_abPathIdx].p).c_str(), _abFailoverQueue.size());
}
continue;
}
else {
++it;
@ -1345,7 +1473,9 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
continue;
}
SharedPtr<Link> link = RR->bc->getLinkBySocket(_policyAlias, _paths[i].p->localSocket());
if (! link) {
continue;
}
int failoverScoreHandicap = _paths[i].failoverScore;
if (_paths[i].preferred()) {
failoverScoreHandicap += ZT_BOND_FAILOVER_HANDICAP_PREFERRED;
@ -1375,17 +1505,19 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
}
}
}
if (_paths[i].p.ptr() != _paths[_abPathIdx].p.ptr()) {
bool bFoundPathInQueue = false;
for (std::deque<int>::iterator it(_abFailoverQueue.begin()); it != _abFailoverQueue.end(); ++it) {
if (_paths[i].p.ptr() == _paths[(*it)].p.ptr()) {
bFoundPathInQueue = true;
if (_paths[i].p) {
if (_paths[i].p.ptr() != _paths[_abPathIdx].p.ptr()) {
bool bFoundPathInQueue = false;
for (std::deque<int>::iterator it(_abFailoverQueue.begin()); it != _abFailoverQueue.end(); ++it) {
if (_paths[(*it)].p && (_paths[i].p.ptr() == _paths[(*it)].p.ptr())) {
bFoundPathInQueue = true;
}
}
if (! bFoundPathInQueue) {
_abFailoverQueue.push_front(i);
log("add link %s to failover queue (%zu links in queue)", pathToStr(_paths[i].p).c_str(), _abFailoverQueue.size());
addPathToBond(0, i);
}
}
if (! bFoundPathInQueue) {
_abFailoverQueue.push_front(i);
log("add link %s to failover queue (%zu links in queue)", pathToStr(_paths[i].p).c_str(), _abFailoverQueue.size());
addPathToBond(0, i);
}
}
}
@ -1406,7 +1538,11 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
if (! _paths[i].eligible) {
failoverScoreHandicap = -10000;
}
if (getLink(_paths[i].p)->primary() && _abLinkSelectMethod != ZT_BOND_RESELECTION_POLICY_OPTIMIZE) {
SharedPtr<Link> link = getLink(_paths[i].p);
if (! link) {
continue;
}
if (link->primary() && _abLinkSelectMethod != ZT_BOND_RESELECTION_POLICY_OPTIMIZE) {
// If using "optimize" primary re-select mode, ignore user link designations
failoverScoreHandicap = ZT_BOND_FAILOVER_HANDICAP_PRIMARY;
}
@ -1458,7 +1594,7 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
/**
* Fulfill primary re-select obligations
*/
if (_paths[_abPathIdx].p && ! _paths[_abPathIdx].eligible) { // Implicit ZT_BOND_RESELECTION_POLICY_FAILURE
if (! _paths[_abPathIdx].eligible) { // Implicit ZT_BOND_RESELECTION_POLICY_FAILURE
log("link %s has failed, select link from failover queue (%zu links in queue)", pathToStr(_paths[_abPathIdx].p).c_str(), _abFailoverQueue.size());
if (! _abFailoverQueue.empty()) {
dequeueNextActiveBackupPath(now);
@ -1474,16 +1610,29 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
if (prevActiveBackupPathIdx != _abPathIdx) {
_lastActiveBackupPathChange = now;
}
if (_abFailoverQueue.empty()) {
return; // No sense in continuing since there are no links to switch to
}
if (_abLinkSelectMethod == ZT_BOND_RESELECTION_POLICY_ALWAYS) {
if (_paths[_abPathIdx].p && ! getLink(_paths[_abPathIdx].p)->primary() && getLink(_paths[_abFailoverQueue.front()].p)->primary()) {
SharedPtr<Link> abLink = getLink(_paths[_abPathIdx].p);
if (! _paths[_abFailoverQueue.front()].p) {
log("invalid link. not switching");
return;
}
SharedPtr<Link> abFailoverLink = getLink(_paths[_abFailoverQueue.front()].p);
if (abLink && ! abLink->primary() && _paths[_abFailoverQueue.front()].p && abFailoverLink && abFailoverLink->primary()) {
dequeueNextActiveBackupPath(now);
log("switch back to available primary link %s (select mode: always)", pathToStr(_paths[_abPathIdx].p).c_str());
}
}
if (_abLinkSelectMethod == ZT_BOND_RESELECTION_POLICY_BETTER) {
if (_paths[_abPathIdx].p && ! getLink(_paths[_abPathIdx].p)->primary()) {
SharedPtr<Link> abLink = getLink(_paths[_abPathIdx].p);
if (abLink && ! abLink->primary()) {
// Active backup has switched to "better" primary link according to re-select policy.
if (getLink(_paths[_abFailoverQueue.front()].p)->primary() && (_paths[_abFailoverQueue.front()].failoverScore > _paths[_abPathIdx].failoverScore)) {
SharedPtr<Link> abFailoverLink = getLink(_paths[_abFailoverQueue.front()].p);
if (_paths[_abFailoverQueue.front()].p && abFailoverLink && abFailoverLink->primary() && (_paths[_abFailoverQueue.front()].failoverScore > _paths[_abPathIdx].failoverScore)) {
dequeueNextActiveBackupPath(now);
log("switch back to user-defined primary link %s (select mode: better)", pathToStr(_paths[_abPathIdx].p).c_str());
}
@ -1510,11 +1659,7 @@ void Bond::processActiveBackupTasks(void* tPtr, int64_t now)
if ((failoverScoreDifference > 0) && (failoverScoreDifference > thresholdQuantity)) {
SharedPtr<Path> oldPath = _paths[_abPathIdx].p;
dequeueNextActiveBackupPath(now);
log("switch from %s (score: %d) to better link %s (score: %d) (select mode: optimize)",
pathToStr(oldPath).c_str(),
prevFScore,
pathToStr(_paths[_abPathIdx].p).c_str(),
newFScore);
log("switch from %s (score: %d) to better link %s (score: %d) (select mode: optimize)", pathToStr(oldPath).c_str(), prevFScore, pathToStr(_paths[_abPathIdx].p).c_str(), newFScore);
}
}
}
@ -1530,6 +1675,7 @@ void Bond::initTimers()
_lastPathNegotiationCheck = 0;
_lastPathNegotiationReceived = 0;
_lastQoSRateCheck = 0;
_lastAckRateCheck = 0;
_lastQualityEstimation = 0;
_lastBondStatusLog = 0;
_lastSummaryDump = 0;
@ -1562,10 +1708,6 @@ void Bond::setBondParameters(int policy, SharedPtr<Bond> templateBond, bool useT
_localUtility = 0;
_negotiatedPathIdx = 0;
// QOS Verb (and related checks)
_qosCutoffCount = 0;
// User preferences which may override the default bonding algorithm's behavior
_userHasSpecifiedPrimaryLink = false;
@ -1574,7 +1716,6 @@ void Bond::setBondParameters(int policy, SharedPtr<Bond> templateBond, bool useT
// Bond status
_isHealthy = false;
_numAliveLinks = 0;
_numTotalLinks = 0;
_numBondedPaths = 0;
@ -1650,7 +1791,7 @@ void Bond::setBondParameters(int policy, SharedPtr<Bond> templateBond, bool useT
memcpy(_qw, templateBond->_qw, ZT_QOS_WEIGHT_SIZE * sizeof(float));
}
if (!_isLeaf) {
if (! _isLeaf) {
_policy = ZT_BOND_POLICY_ACTIVE_BACKUP;
}
@ -1659,7 +1800,9 @@ void Bond::setBondParameters(int policy, SharedPtr<Bond> templateBond, bool useT
_monitorInterval = _failoverInterval / ZT_BOND_ECHOS_PER_FAILOVER_INTERVAL;
_qualityEstimationInterval = _failoverInterval * 2;
_qosSendInterval = _failoverInterval * 2;
_ackSendInterval = _failoverInterval * 2;
_qosCutoffCount = 0;
_ackCutoffCount = 0;
_defaultPathRefractoryPeriod = 8000;
}
@ -1678,17 +1821,24 @@ void Bond::setUserQualityWeights(float weights[], int len)
SharedPtr<Link> Bond::getLink(const SharedPtr<Path>& path)
{
return RR->bc->getLinkBySocket(_policyAlias, path->localSocket());
return ! path ? SharedPtr<Link>() : RR->bc->getLinkBySocket(_policyAlias, path->localSocket());
}
std::string Bond::pathToStr(const SharedPtr<Path>& path)
{
#ifdef ZT_TRACE
char pathStr[64] = { 0 };
char fullPathStr[384] = { 0 };
path->address().toString(pathStr);
snprintf(fullPathStr, 384, "%.16llx-%s/%s", (unsigned long long)(path->localSocket()), getLink(path)->ifname().c_str(), pathStr);
return std::string(fullPathStr);
if (path) {
char pathStr[64] = { 0 };
char fullPathStr[384] = { 0 };
path->address().toString(pathStr);
SharedPtr<Link> link = getLink(path);
if (link) {
std::string ifnameStr = std::string(link->ifname());
snprintf(fullPathStr, 384, "%.16llx-%s/%s", (unsigned long long)(path->localSocket()), ifnameStr.c_str(), pathStr);
return std::string(fullPathStr);
}
}
return "";
#else
return "";
#endif
@ -1700,11 +1850,11 @@ void Bond::dumpPathStatus(int64_t now, int pathIdx)
std::string aliveOrDead = _paths[pathIdx].alive ? std::string("alive") : std::string("dead");
std::string eligibleOrNot = _paths[pathIdx].eligible ? std::string("eligible") : std::string("ineligible");
std::string bondedOrNot = _paths[pathIdx].bonded ? std::string("bonded") : std::string("unbonded");
log("path[%2d] --- %5s (in %7d, out: %7d), %10s, %8s, flows=%-6d lat=%-8.3f pdv=%-7.3f err=%-6.4f loss=%-6.4f alloc=%-3d --- (%s)",
log("path[%2u] --- %5s (in %7lld, out: %7lld), %10s, %8s, flows=%-6u lat=%-8.3f pdv=%-7.3f err=%-6.4f loss=%-6.4f alloc=%-3u --- (%s)",
pathIdx,
aliveOrDead.c_str(),
_paths[pathIdx].p->age(now),
(now - _paths[pathIdx].p->_lastOut),
static_cast<long long int>(_paths[pathIdx].p->age(now)),
static_cast<long long int>(_paths[pathIdx].p->_lastOut == 0 ? 0 : now - _paths[pathIdx].p->_lastOut),
eligibleOrNot.c_str(),
bondedOrNot.c_str(),
_paths[pathIdx].assignedFlowCount,
@ -1727,7 +1877,7 @@ void Bond::dumpInfo(int64_t now, bool force)
_lastSummaryDump = now;
float overhead = (_overheadBytes / (timeSinceLastDump / 1000.0f) / 1000.0f);
_overheadBytes = 0;
log("bond: bp=%d, fi=%d, mi=%d, ud=%d, dd=%d, flows=%lu, leaf=%d, overhead=%f KB/s",
log("bond: bp=%d, fi=%d, mi=%d, ud=%d, dd=%d, flows=%lu, leaf=%d, overhead=%f KB/s, links=(%d/%d)",
_policy,
_failoverInterval,
_monitorInterval,
@ -1735,7 +1885,9 @@ void Bond::dumpInfo(int64_t now, bool force)
_downDelay,
(unsigned long)_flows.size(),
_isLeaf,
overhead);
overhead,
_numAliveLinks,
_numTotalLinks);
for (int i = 0; i < ZT_MAX_PEER_NETWORK_PATHS; ++i) {
if (_paths[i].p) {
dumpPathStatus(now, i);

View file

@ -436,7 +436,14 @@ class Bond {
* @param peer Remote peer that this bond services
* @return A pointer to the newly created Bond
*/
static SharedPtr<Bond> createTransportTriggeredBond(const RuntimeEnvironment* renv, const SharedPtr<Peer>& peer);
static SharedPtr<Bond> createBond(const RuntimeEnvironment* renv, const SharedPtr<Peer>& peer);
/**
* Remove a bond from the bond controller.
*
* @param peerId Remote peer that this bond services
*/
static void destroyBond(uint64_t peerId);
/**
* Periodically perform maintenance tasks for the bonding layer.
@ -451,9 +458,10 @@ class Bond {
*
* @param policyAlias Policy in use
* @param localSocket Local source socket
* @param createIfNeeded Whether a Link object is created if the name wasn't previously in the link map
* @return Physical link definition
*/
static SharedPtr<Link> getLinkBySocket(const std::string& policyAlias, uint64_t localSocket);
static SharedPtr<Link> getLinkBySocket(const std::string& policyAlias, uint64_t localSocket, bool createIfNeeded);
/**
* Gets a reference to a physical link definition given its human-readable system name.
@ -630,6 +638,15 @@ class Bond {
*/
void receivedQoS(const SharedPtr<Path>& path, int64_t now, int count, uint64_t* rx_id, uint16_t* rx_ts);
/**
* Process the contents of an inbound VERB_ACK to gather path quality observations.
*
* @param pathIdx Path over which packet was received
* @param now Current time
* @param ackedBytes Number of bytes ACKed by this VERB_ACK
*/
void receivedAck(int pathIdx, int64_t now, int32_t ackedBytes);
/**
* Generate the contents of a VERB_QOS_MEASUREMENT packet.
*
@ -871,6 +888,26 @@ class Bond {
*/
void processBackgroundBondTasks(void* tPtr, int64_t now);
/**
* Rate limit gate for VERB_ACK
*
* @param now Current time
* @return Whether the incoming packet should be rate-gated
*/
inline bool rateGateACK(const int64_t now)
{
_ackCutoffCount++;
int numToDrain = _lastAckRateCheck ? (now - _lastAckRateCheck) / ZT_ACK_DRAINAGE_DIVISOR : _ackCutoffCount;
_lastAckRateCheck = now;
if (_ackCutoffCount > numToDrain) {
_ackCutoffCount -= numToDrain;
}
else {
_ackCutoffCount = 0;
}
return (_ackCutoffCount < ZT_ACK_CUTOFF_LIMIT);
}
/**
* Rate limit gate for VERB_QOS_MEASUREMENT
*
@ -1020,14 +1057,6 @@ class Bond {
return _policy;
}
/**
* @return the health status of the bond
*/
inline bool isHealthy()
{
return _isHealthy;
}
/**
* @return the number of links comprising this bond which are considered alive
*/
@ -1142,6 +1171,9 @@ class Bond {
*
*/
void log(const char* fmt, ...)
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
{
#ifdef ZT_TRACE
time_t rawtime;
@ -1171,6 +1203,9 @@ class Bond {
*
*/
void debug(const char* fmt, ...)
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
{
#ifdef ZT_DEBUG
time_t rawtime;
@ -1198,7 +1233,11 @@ class Bond {
private:
struct NominatedPath {
NominatedPath()
: lastQoSMeasurement(0)
: lastAckSent(0)
, lastAckReceived(0)
, unackedBytes(0)
, packetsReceivedSinceLastAck(0)
, lastQoSMeasurement(0)
, lastThroughputEstimation(0)
, lastRefractoryUpdate(0)
, lastAliveToggle(0)
@ -1289,6 +1328,15 @@ class Bond {
return ((packetsReceivedSinceLastQoS >= ZT_QOS_TABLE_SIZE) || ((now - lastQoSMeasurement) > qosSendInterval)) && packetsReceivedSinceLastQoS;
}
/**
* @param now Current time
* @return Whether an ACK (VERB_ACK) packet needs to be emitted at this time
*/
inline bool needsToSendAck(int64_t now, int ackSendInterval)
{
return ((now - lastAckSent) >= ackSendInterval || (packetsReceivedSinceLastAck == ZT_QOS_TABLE_SIZE)) && packetsReceivedSinceLastAck;
}
/**
* Reset packet counters
*/
@ -1300,6 +1348,7 @@ class Bond {
std::map<uint64_t, uint64_t> qosStatsOut; // id:egress_time
std::map<uint64_t, uint64_t> qosStatsIn; // id:now
std::map<uint64_t, uint64_t> ackStatsIn; // id:now
RingBuffer<int, ZT_QOS_SHORTTERM_SAMPLE_WIN_SIZE> qosRecordSize;
RingBuffer<float, ZT_QOS_SHORTTERM_SAMPLE_WIN_SIZE> qosRecordLossSamples;
@ -1308,6 +1357,11 @@ class Bond {
RingBuffer<float, ZT_QOS_SHORTTERM_SAMPLE_WIN_SIZE> throughputVarianceSamples;
RingBuffer<uint16_t, ZT_QOS_SHORTTERM_SAMPLE_WIN_SIZE> latencySamples;
uint64_t lastAckSent;
uint64_t lastAckReceived;
uint64_t unackedBytes;
uint64_t packetsReceivedSinceLastAck;
uint64_t lastQoSMeasurement; // Last time that a VERB_QOS_MEASUREMENT was sent out on this path.
uint64_t lastThroughputEstimation; // Last time that the path's throughput was estimated.
uint64_t lastRefractoryUpdate; // The last time that the refractory period was updated.
@ -1344,7 +1398,7 @@ class Bond {
int packetsIn;
int packetsOut;
AtomicCounter __refCount;
// AtomicCounter __refCount;
SharedPtr<Path> p;
void set(uint64_t now, const SharedPtr<Path>& path)
@ -1490,7 +1544,6 @@ class Bond {
/**
* Link state reporting
*/
bool _isHealthy;
uint8_t _numAliveLinks;
uint8_t _numTotalLinks;
@ -1508,7 +1561,9 @@ class Bond {
* Rate-limiting
*/
uint16_t _qosCutoffCount;
uint16_t _ackCutoffCount;
uint64_t _lastQoSRateCheck;
uint64_t _lastAckRateCheck;
uint16_t _pathNegotiationCutoffCount;
uint64_t _lastPathNegotiationReceived;

View file

@ -50,16 +50,17 @@
#define __UNIX_LIKE__
#endif
#include <endian.h>
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(__aarch64__))
#define OIDC_SUPPORTED 1
#else
#define OIDC_SUPPORTED 0
#ifdef ZT_SSO_SUPPORTED
#define ZT_SSO_ENABLED 1
#endif
#endif
#endif
#ifdef __APPLE__
#define OIDC_SUPPORTED 1
#ifdef ZT_SSO_SUPPORTED
#define ZT_SSO_ENABLED 1
#endif
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
#include <TargetConditionals.h>
@ -73,7 +74,9 @@
#endif
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#define OIDC_SUPPORTED 0
#ifdef ZT_SSO_SUPPORTED
#define ZT_SSO_ENABLED 0
#endif
#ifndef __UNIX_LIKE__
#define __UNIX_LIKE__
#endif
@ -89,7 +92,9 @@
#endif
#if defined(_WIN32) || defined(_WIN64)
#define OIDC_SUPPORTED 1
#ifdef ZT_SSO_SUPPORTED
#define ZT_SSO_ENABLED 1
#endif
#ifndef __WINDOWS__
#define __WINDOWS__
#endif
@ -368,9 +373,14 @@
#define ZT_QOS_TABLE_SIZE ((ZT_QOS_MAX_PACKET_SIZE * 8) / (64 + 16))
/**
* Maximum number of outgoing packets we monitor for QoS information
* Maximum number of packets we monitor for ACK information at any given time
*/
#define ZT_QOS_MAX_OUTSTANDING_RECORDS (1024 * 16)
#define ZT_ACK_MAX_PENDING_RECORDS (32 * 1024)
/**
* Maximum number of packets we monitor for QoS information at any given time
*/
#define ZT_QOS_MAX_PENDING_RECORDS (ZT_QOS_TABLE_SIZE * 3)
/**
* Interval used for rate-limiting the computation of path quality estimates.
@ -525,14 +535,14 @@
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8
/**
* Time horizon for VERB_NETWORK_CREDENTIALS cutoff
* Rate limit for network credential pushes from peer.
*/
#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
#define ZT_PEER_CREDENTIALS_RATE_LIMIT 1000
/**
* Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
* Rate limit for responding to peer credential requests
*/
#define ZT_PEER_CREDENTIALS_CUTOFF_LIMIT 15
#define ZT_PEER_CREDENTIALS_REQUEST_RATE_LIMIT 1000
/**
* WHOIS rate limit (we allow these to be pretty fast)
@ -571,13 +581,13 @@
* Anything below this value gets into thrashing territory since we divide
* this value by ZT_BOND_ECHOS_PER_FAILOVER_INTERVAL to send ECHOs often.
*/
#define ZT_BOND_FAILOVER_MIN_INTERVAL 250
#define ZT_BOND_FAILOVER_MIN_INTERVAL 500
/**
* How many times per failover interval that an ECHO is sent. This should be
* at least 2. Anything more then 4 starts to increase overhead significantly.
*/
#define ZT_BOND_ECHOS_PER_FAILOVER_INTERVAL 4
#define ZT_BOND_ECHOS_PER_FAILOVER_INTERVAL 3
/**
* A defensive timer to prevent path quality metrics from being

View file

@ -47,14 +47,13 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
try {
// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
const unsigned int c = cipher();
bool trusted = false;
if (c == ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH) {
// If this is marked as a packet via a trusted path, check source address and path ID.
// Obviously if no trusted paths are configured this always returns false and such
// packets are dropped on the floor.
const uint64_t tpid = trustedPathId();
if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
trusted = true;
_authenticated = true;
} else {
RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
return true;
@ -66,7 +65,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,sourceAddress));
if (peer) {
if (!trusted) {
if (!_authenticated) {
if (!dearmor(peer->key(), peer->aesKeys())) {
RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
peer->recordIncomingInvalidPacket(_path);
@ -79,6 +78,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
return true;
}
_authenticated = true;
const Packet::Verb v = verb();
bool r = true;
@ -88,6 +88,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,false,0,ZT_QOS_NO_FLOW);
break;
case Packet::VERB_HELLO: r = _doHELLO(RR,tPtr,true); break;
case Packet::VERB_ACK : r = _doACK(RR,tPtr,peer); break;
case Packet::VERB_QOS_MEASUREMENT: r = _doQOS_MEASUREMENT(RR,tPtr,peer); break;
case Packet::VERB_ERROR: r = _doERROR(RR,tPtr,peer); break;
case Packet::VERB_OK: r = _doOK(RR,tPtr,peer); break;
@ -169,7 +170,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar
const SharedPtr<Network> network(RR->node->network(networkId));
const int64_t now = RR->node->now();
if ((network)&&(network->config().com))
network->pushCredentialsNow(tPtr,peer->address(),now);
network->peerRequestedCredentials(tPtr,peer->address(),now);
} break;
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
@ -250,28 +251,47 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar
return true;
}
bool IncomingPacket::_doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
bool IncomingPacket::_doACK(const RuntimeEnvironment* RR, void* tPtr, const SharedPtr<Peer>& peer)
{
/*
SharedPtr<Bond> bond = peer->bond();
if (! bond || ! bond->rateGateACK(RR->node->now())) {
return true;
}
int32_t ackedBytes;
if (payloadLength() != sizeof(ackedBytes)) {
return true; // ignore
}
memcpy(&ackedBytes, payload(), sizeof(ackedBytes));
if (bond) {
bond->receivedAck(_path, RR->node->now(), Utils::ntoh(ackedBytes));
}
*/
return true;
}
bool IncomingPacket::_doQOS_MEASUREMENT(const RuntimeEnvironment* RR, void* tPtr, const SharedPtr<Peer>& peer)
{
SharedPtr<Bond> bond = peer->bond();
if (!bond || !bond->rateGateQoS(RR->node->now(), _path)) {
if (! bond || ! bond->rateGateQoS(RR->node->now(), _path)) {
return true;
}
if (payloadLength() > ZT_QOS_MAX_PACKET_SIZE || payloadLength() < ZT_QOS_MIN_PACKET_SIZE) {
return true; // ignore
return true; // ignore
}
const int64_t now = RR->node->now();
uint64_t rx_id[ZT_QOS_TABLE_SIZE];
uint16_t rx_ts[ZT_QOS_TABLE_SIZE];
char *begin = (char *)payload();
char *ptr = begin;
char* begin = (char*)payload();
char* ptr = begin;
int count = 0;
unsigned int len = payloadLength();
// Read packet IDs and latency compensation intervals for each packet tracked by this QoS packet
while (ptr < (begin + len) && (count < ZT_QOS_TABLE_SIZE)) {
memcpy((void*)&rx_id[count], ptr, sizeof(uint64_t));
ptr+=sizeof(uint64_t);
ptr += sizeof(uint64_t);
memcpy((void*)&rx_ts[count], ptr, sizeof(uint16_t));
ptr+=sizeof(uint16_t);
ptr += sizeof(uint16_t);
count++;
}
if (bond) {
@ -860,7 +880,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
uint64_t now = RR->node->now();
if (!peer->rateGateEchoRequest(now)) {
if (!_path->rateGateEchoRequest(now)) {
return true;
}
@ -1057,10 +1077,8 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c
{
const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
if (network) {
//fprintf(stderr, "IncomingPacket::_doNETWORK_CONFIG %.16llx\n", network->id());
const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
if (configUpdateId) {
//fprintf(stderr, "Have config update ID: %llu\n", configUpdateId);
Packet outp(peer->address(), RR->identity.address(), Packet::VERB_OK);
outp.append((uint8_t)Packet::VERB_ECHO);
outp.append((uint64_t)packetId());
@ -1069,9 +1087,7 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c
const int64_t now = RR->node->now();
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
if (!_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now())) {
//fprintf(stderr, "Error sending VERB_OK after NETWORK_CONFIG packet for %.16llx\n", network->id());
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
@ -1098,16 +1114,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
} catch ( ... ) {} // discard invalid COMs
}
bool trustEstablished = false;
if (network) {
if (network->gate(tPtr,peer)) {
trustEstablished = true;
} else {
_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
return false;
}
}
const bool trustEstablished = (network) ? network->gate(tPtr,peer) : false;
const int64_t now = RR->node->now();
if ((gatherLimit > 0)&&((trustEstablished)||(RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())))) {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
@ -1224,9 +1231,6 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
}
peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid,ZT_QOS_NO_FLOW);
} else {
_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
return false;
}
return true;

View file

@ -51,7 +51,9 @@ class IncomingPacket : public Packet
public:
IncomingPacket() :
Packet(),
_receiveTime(0)
_receiveTime(0),
_path(),
_authenticated(false)
{
}
@ -67,7 +69,8 @@ public:
IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now) :
Packet(data,len),
_receiveTime(now),
_path(path)
_path(path),
_authenticated(false)
{
}
@ -85,6 +88,7 @@ public:
copyFrom(data,len);
_receiveTime = now;
_path = path;
_authenticated = false;
}
/**
@ -112,6 +116,7 @@ private:
// been authenticated, decrypted, decompressed, and classified.
bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
bool _doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
@ -134,6 +139,7 @@ private:
uint64_t _receiveTime;
SharedPtr<Path> _path;
bool _authenticated;
};
} // namespace ZeroTier

View file

@ -115,7 +115,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
RR->t->credentialRejected(tPtr,com,"old");
return ADD_REJECTED;
}
if ((newts == oldts)&&(_com == com))
if (_com == com)
return ADD_ACCEPTED_REDUNDANT;
switch(com.verify(RR,tPtr)) {
@ -123,6 +123,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
RR->t->credentialRejected(tPtr,com,"invalid");
return ADD_REJECTED;
case 0:
//printf("%.16llx %.10llx replacing COM %lld with %lld\n", com.networkId(), com.issuedTo().toInt(), _com.timestamp(), com.timestamp()); fflush(stdout);
_com = com;
return ADD_ACCEPTED_NEW;
case 1:

View file

@ -64,13 +64,9 @@ public:
*/
void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf);
/**
* @return True if we haven't pushed credentials in a long time (to cause proactive credential push)
*/
inline bool shouldPushCredentials(const int64_t now) const
{
return ((now - _lastPushedCredentials) > ZT_PEER_ACTIVITY_TIMEOUT);
}
inline int64_t lastPushedCredentials() { return _lastPushedCredentials; }
inline int64_t comTimestamp() { return _com.timestamp(); }
inline int64_t comRevocationThreshold() { return _comRevocationThreshold; }
/**
* Check whether we should push MULTICAST_LIKEs to this peer, and update last sent time if true
@ -96,9 +92,7 @@ public:
*/
inline bool isAllowedOnNetwork(const NetworkConfig &thisNodeNetworkConfig, const Identity &otherNodeIdentity) const
{
if (thisNodeNetworkConfig.isPublic()) return true;
if (_com.timestamp() <= _comRevocationThreshold) return false;
return thisNodeNetworkConfig.com.agreesWith(_com, otherNodeIdentity);
return thisNodeNetworkConfig.isPublic() || (((_com.timestamp() > _comRevocationThreshold) && (thisNodeNetworkConfig.com.agreesWith(_com, otherNodeIdentity))));
}
inline bool recentlyAssociated(const int64_t now) const

View file

@ -1223,10 +1223,16 @@ void Network::requestConfiguration(void *tPtr)
bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
{
const int64_t now = RR->node->now();
//int64_t comTimestamp = 0;
//int64_t comRevocationThreshold = 0;
Mutex::Lock _l(_lock);
try {
if (_config) {
Membership *m = _memberships.get(peer->address());
//if (m) {
// comTimestamp = m->comTimestamp();
// comRevocationThreshold = m->comRevocationThreshold();
//}
if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config, peer->identity()))) ) {
if (!m)
m = &(_membership(peer->address()));
@ -1237,6 +1243,8 @@ bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
}
}
} catch ( ... ) {}
//printf("%.16llx %.10llx not allowed, COM ts %lld revocation %lld\n", _id, peer->address().toInt(), comTimestamp, comRevocationThreshold); fflush(stdout);
return false;
}

View file

@ -372,10 +372,13 @@ public:
* @param to Destination peer address
* @param now Current time
*/
inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now)
inline void peerRequestedCredentials(void *tPtr,const Address &to,const int64_t now)
{
Mutex::Lock _l(_lock);
_membership(to).pushCredentials(RR,tPtr,now,to,_config);
Membership &m = _membership(to);
const int64_t lastPushed = m.lastPushedCredentials();
if ((lastPushed < _lastConfigUpdate)||((now - lastPushed) > ZT_PEER_CREDENTIALS_REQUEST_RATE_LIMIT))
m.pushCredentials(RR,tPtr,now,to,_config);
}
/**
@ -389,7 +392,8 @@ public:
{
Mutex::Lock _l(_lock);
Membership &m = _membership(to);
if (m.shouldPushCredentials(now))
const int64_t lastPushed = m.lastPushedCredentials();
if ((lastPushed < _lastConfigUpdate)||((now - lastPushed) > ZT_PEER_ACTIVITY_TIMEOUT))
m.pushCredentials(RR,tPtr,now,to,_config);
}
@ -439,7 +443,7 @@ private:
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
NetworkConfig _config;
uint64_t _lastConfigUpdate;
int64_t _lastConfigUpdate;
struct _IncomingConfigChunk
{

View file

@ -41,20 +41,19 @@
#include "Trace.hpp"
/**
* Default maximum time delta for COMs, tags, and capabilities
*
* The current value is two hours, providing ample time for a controller to
* experience fail-over, etc.
* Default time delta for COMs, tags, and capabilities
*/
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA 7200000ULL
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_DFL_MAX_DELTA ((int64_t)(1000 * 60 * 30))
/**
* Default minimum credential TTL and maxDelta for COM timestamps
*
* This is just slightly over three minutes and provides three retries for
* all currently online members to refresh.
* Maximum time delta for COMs, tags, and capabilities
*/
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA 185000ULL
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA ((int64_t)(1000 * 60 * 60 * 2))
/**
* Minimum credential TTL and maxDelta for COM timestamps
*/
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA ((int64_t)(1000 * 60 * 5))
/**
* Flag: enable broadcast

View file

@ -509,7 +509,6 @@ ZT_PeerList *Node::peers() const
if (pi->second->bond()) {
p->isBonded = pi->second->bond();
p->bondingPolicy = pi->second->bond()->policy();
p->isHealthy = pi->second->bond()->isHealthy();
p->numAliveLinks = pi->second->bond()->getNumAliveLinks();
p->numTotalLinks = pi->second->bond()->getNumTotalLinks();
}
@ -741,7 +740,7 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
n->setAccessDenied(nullptr);
break;
case NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED: {
fprintf(stderr, "\n\nGot auth required\n\n");
//fprintf(stderr, "\n\nGot auth required\n\n");
break;
}

View file

@ -83,6 +83,7 @@ public:
_lastOut(0),
_lastIn(0),
_lastTrustEstablishedPacketReceived(0),
_lastEchoRequestReceived(0),
_localSocket(-1),
_latency(0xffff),
_addr(),
@ -93,6 +94,7 @@ public:
_lastOut(0),
_lastIn(0),
_lastTrustEstablishedPacketReceived(0),
_lastEchoRequestReceived(0),
_localSocket(localSocket),
_latency(0xffff),
_addr(addr),
@ -266,6 +268,18 @@ public:
*/
inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; }
/**
* Rate limit gate for inbound ECHO requests
*/
inline bool rateGateEchoRequest(const int64_t now)
{
if ((now - _lastEchoRequestReceived) >= (ZT_PEER_GENERAL_RATE_LIMIT / 6)) {
_lastEchoRequestReceived = now;
return true;
}
return false;
}
void *_bondingMetricPtr;
private:
@ -273,6 +287,9 @@ private:
volatile int64_t _lastOut;
volatile int64_t _lastIn;
volatile int64_t _lastTrustEstablishedPacketReceived;
int64_t _lastEchoRequestReceived;
int64_t _localSocket;
volatile unsigned int _latency;
InetAddress _addr;

View file

@ -34,7 +34,6 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
_lastTriedMemorizedPath(0),
_lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
_lastEchoRequestReceived(0),
_lastCredentialRequestSent(0),
_lastWhoisRequestReceived(0),
_lastCredentialsReceived(0),
@ -48,7 +47,6 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
_vRevision(0),
_id(peerIdentity),
_directPathPushCutoffCount(0),
_credentialsCutoffCount(0),
_echoRequestCutoffCount(0),
_localMultipathSupported(false),
_lastComputedAggregateMeanLatency(0)
@ -224,6 +222,8 @@ void Peer::received(
if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) {
_lastDirectPathPushSent = now;
std::vector<InetAddress> pathsToPush(RR->node->directPaths());
std::vector<InetAddress> ma = RR->sa->whoami();
pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end());
if (!pathsToPush.empty()) {
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
while (p != pathsToPush.end()) {
@ -453,7 +453,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,localSocket,atAddress,outp.data(),outp.size());
RR->node->putPacket(tPtr,-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
@ -502,7 +502,7 @@ void Peer::performMultipathStateCheck(void *tPtr, int64_t now)
_localMultipathSupported = ((numAlivePaths >= 1) && (RR->bc->inUse()) && (ZT_PROTO_VERSION > 9));
if (_localMultipathSupported && !_bond) {
if (RR->bc) {
_bond = RR->bc->createTransportTriggeredBond(RR, this);
_bond = RR->bc->createBond(RR, this);
/**
* Allow new bond to retroactively learn all paths known to this peer
*/

View file

@ -53,7 +53,10 @@ private:
Peer() {} // disabled to prevent bugs -- should not be constructed uninitialized
public:
~Peer() { Utils::burn(_key,sizeof(_key)); }
~Peer() {
Utils::burn(_key,sizeof(_key));
RR->bc->destroyBond(_id.address().toInt());
}
/**
* Construct a new peer
@ -387,11 +390,11 @@ public:
*/
inline bool rateGateCredentialsReceived(const int64_t now)
{
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
++_credentialsCutoffCount;
else _credentialsCutoffCount = 0;
_lastCredentialsReceived = now;
return (_credentialsCutoffCount < ZT_PEER_CREDENTIALS_CUTOFF_LIMIT);
if ((now - _lastCredentialsReceived) >= ZT_PEER_CREDENTIALS_RATE_LIMIT) {
_lastCredentialsReceived = now;
return true;
}
return false;
}
/**
@ -418,18 +421,6 @@ public:
return false;
}
/**
* Rate limit gate for inbound ECHO requests
*/
inline bool rateGateEchoRequest(const int64_t now)
{
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastEchoRequestReceived = now;
return true;
}
return false;
}
/**
* Serialize a peer for storage in local cache
*
@ -546,7 +537,6 @@ private:
int64_t _lastTriedMemorizedPath;
int64_t _lastDirectPathPushSent;
int64_t _lastDirectPathPushReceive;
int64_t _lastEchoRequestReceived;
int64_t _lastCredentialRequestSent;
int64_t _lastWhoisRequestReceived;
int64_t _lastCredentialsReceived;
@ -573,7 +563,6 @@ private:
Identity _id;
unsigned int _directPathPushCutoffCount;
unsigned int _credentialsCutoffCount;
unsigned int _echoRequestCutoffCount;
AtomicCounter __refCount;

View file

@ -67,7 +67,7 @@ public:
* @param tgt Target node whose credential(s) are being revoked
* @param ct Credential type being revoked
*/
Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) :
Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const int64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) :
_id(i),
_credentialId(cid),
_networkId(nwid),
@ -155,7 +155,7 @@ public:
_networkId = b.template at<uint64_t>(p); p += 8;
p += 4; // 4 bytes, currently unused
_credentialId = b.template at<uint32_t>(p); p += 4;
_threshold = b.template at<uint64_t>(p); p += 8;
_threshold = (int64_t)b.template at<uint64_t>(p); p += 8;
_flags = b.template at<uint64_t>(p); p += 8;
_target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;

View file

@ -15,7 +15,7 @@
#include "Constants.hpp"
#include "Utils.hpp"
#if (!defined(ZT_SALSA20_SSE)) && (defined(__SSE2__) || (defined(__WINDOWS__) && !defined(__MINGW32__)))
#if (!defined(ZT_SALSA20_SSE)) && (defined(__SSE2__) || (defined(__WINDOWS__) && !defined(__MINGW32__) && !defined(_M_ARM64)))
#define ZT_SALSA20_SSE 1
#endif

View file

@ -99,6 +99,21 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receive
}
}
std::vector<InetAddress> SelfAwareness::whoami()
{
std::vector<InetAddress> surfaceAddresses;
Mutex::Lock _l(_phy_m);
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
PhySurfaceKey *k = (PhySurfaceKey *)0;
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
while (i.next(k,e)) {
if (std::find(surfaceAddresses.begin(), surfaceAddresses.end(), e->mySurface) == surfaceAddresses.end()) {
surfaceAddresses.push_back(e->mySurface);
}
}
return surfaceAddresses;
}
void SelfAwareness::clean(int64_t now)
{
Mutex::Lock _l(_phy_m);

View file

@ -44,6 +44,13 @@ public:
*/
void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
/**
* Return all known external surface addresses reported by peers
*
* @return A vector of InetAddress(es)
*/
std::vector<InetAddress> whoami();
/**
* Clean up database periodically
*

40
one.cpp
View file

@ -88,7 +88,7 @@
#include "service/OneService.hpp"
#include "ext/json/json.hpp"
#include <nlohmann/json.hpp>
#ifdef __APPLE__
#include <SystemConfiguration/SystemConfiguration.h>
@ -523,31 +523,23 @@ static int cli(int argc,char **argv)
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
bool bFoundBond = false;
printf(" <peer> <bondtype> <status> <links>" ZT_EOL_S);
printf(" <peer> <bondtype> <links>" ZT_EOL_S);
if (j.is_array()) {
for(unsigned long k=0;k<j.size();++k) {
nlohmann::json &p = j[k];
bool isBonded = p["isBonded"];
if (isBonded) {
int8_t bondingPolicy = p["bondingPolicy"];
bool isHealthy = p["isHealthy"];
int8_t numAliveLinks = p["numAliveLinks"];
int8_t numTotalLinks = p["numTotalLinks"];
bFoundBond = true;
std::string healthStr;
if (isHealthy) {
healthStr = "HEALTHY";
} else {
healthStr = "DEGRADED";
}
std::string policyStr = "none";
if (bondingPolicy >= ZT_BOND_POLICY_NONE && bondingPolicy <= ZT_BOND_POLICY_BALANCE_AWARE) {
policyStr = Bond::getPolicyStrByCode(bondingPolicy);
}
printf("%10s %32s %8s %d/%d" ZT_EOL_S,
printf("%10s %32s %d/%d" ZT_EOL_S,
OSUtils::jsonString(p ["address"],"-").c_str(),
policyStr.c_str(),
healthStr.c_str(),
numAliveLinks,
numTotalLinks);
}
@ -617,12 +609,6 @@ static int cli(int argc,char **argv)
if (json) {
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
std::string healthStr;
if (OSUtils::jsonInt(j["isHealthy"],0)) {
healthStr = "Healthy";
} else {
healthStr = "Degraded";
}
int numAliveLinks = OSUtils::jsonInt(j["numAliveLinks"],0);
int numTotalLinks = OSUtils::jsonInt(j["numTotalLinks"],0);
printf("Peer : %s\n", arg1.c_str());
@ -630,7 +616,6 @@ static int cli(int argc,char **argv)
//if (bondingPolicy == ZT_BOND_POLICY_ACTIVE_BACKUP) {
printf("Link Select Method : %d\n", (int)OSUtils::jsonInt(j["linkSelectMethod"],0));
//}
printf("Status : %s\n", healthStr.c_str());
printf("Links : %d/%d\n", numAliveLinks, numTotalLinks);
printf("Failover Interval : %d (ms)\n", (int)OSUtils::jsonInt(j["failoverInterval"],0));
printf("Up Delay : %d (ms)\n", (int)OSUtils::jsonInt(j["upDelay"],0));
@ -705,33 +690,23 @@ static int cli(int argc,char **argv)
printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
bool bFoundBond = false;
printf(" <peer> <bondtype> <status> <links>" ZT_EOL_S);
printf(" <peer> <bondtype> <links>" ZT_EOL_S);
if (j.is_array()) {
for(unsigned long k=0;k<j.size();++k) {
nlohmann::json &p = j[k];
bool isBonded = p["isBonded"];
if (isBonded) {
int8_t bondingPolicy = p["bondingPolicy"];
bool isHealthy = p["isHealthy"];
int8_t numAliveLinks = p["numAliveLinks"];
int8_t numTotalLinks = p["numTotalLinks"];
bFoundBond = true;
std::string healthStr;
if (isHealthy) {
healthStr = "Healthy";
} else {
healthStr = "Degraded";
}
std::string policyStr = "none";
if (bondingPolicy >= ZT_BOND_POLICY_NONE && bondingPolicy <= ZT_BOND_POLICY_BALANCE_AWARE) {
policyStr = Bond::getPolicyStrByCode(bondingPolicy);
}
printf("%10s %32s %8s %d/%d" ZT_EOL_S,
printf("%10s %32s %d/%d" ZT_EOL_S,
OSUtils::jsonString(p["address"],"-").c_str(),
policyStr.c_str(),
healthStr.c_str(),
numAliveLinks,
numTotalLinks);
}
@ -800,7 +775,10 @@ static int cli(int argc,char **argv)
if (status == "AUTHENTICATION_REQUIRED") {
printf(" AUTH EXPIRED, URL: %s" ZT_EOL_S, OSUtils::jsonString(n["authenticationURL"], "(null)").c_str());
} else if (status == "OK") {
printf(" AUTH OK, expires in: %lld seconds" ZT_EOL_S, ((int64_t)authenticationExpiryTime - OSUtils::now()) / 1000LL);
int64_t expiresIn = ((int64_t)authenticationExpiryTime - OSUtils::now()) / 1000LL;
if (expiresIn >= 0) {
printf(" AUTH OK, expires in: %lld seconds" ZT_EOL_S, expiresIn);
}
}
}
}

View file

@ -40,7 +40,7 @@
#endif
#endif
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__)
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK)
#include <net/if.h>
#include <netinet6/in6_var.h>
#include <sys/ioctl.h>
@ -311,7 +311,7 @@ class Binder {
if (! gotViaProc) {
struct ifaddrs* ifatbl = (struct ifaddrs*)0;
struct ifaddrs* ifa;
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__)
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK)
// set up an IPv6 socket so we can check the state of interfaces via SIOCGIFAFLAG_IN6
int infoSock = socket(AF_INET6, SOCK_DGRAM, 0);
#endif
@ -320,7 +320,7 @@ class Binder {
while (ifa) {
if ((ifa->ifa_name) && (ifa->ifa_addr)) {
InetAddress ip = *(ifa->ifa_addr);
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__)
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK)
// Check if the address is an IPv6 Temporary Address, macOS/BSD version
if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ifa->ifa_addr;
@ -368,7 +368,7 @@ class Binder {
else {
interfacesEnumerated = false;
}
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__)
#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK)
close(infoSock);
#endif
}

View file

@ -43,7 +43,7 @@
#endif
#ifndef OMIT_JSON_SUPPORT
#include "../ext/json/json.hpp"
#include <nlohmann/json.hpp>
#endif
namespace ZeroTier {

View file

@ -229,23 +229,33 @@ public:
* @param s Socket object
* @return Underlying OS-type (usually int or long) file descriptor associated with object
*/
static inline ZT_PHY_SOCKFD_TYPE getDescriptor(PhySocket *s) throw() { return reinterpret_cast<PhySocketImpl *>(s)->sock; }
static inline ZT_PHY_SOCKFD_TYPE getDescriptor(PhySocket* s) throw()
{
return reinterpret_cast<PhySocketImpl*>(s)->sock;
}
/**
* @param s Socket object
* @return Pointer to user object
*/
static inline void** getuptr(PhySocket *s) throw() { return &(reinterpret_cast<PhySocketImpl *>(s)->uptr); }
static inline void** getuptr(PhySocket* s) throw()
{
return &(reinterpret_cast<PhySocketImpl*>(s)->uptr);
}
/**
* @param s Socket object
* @param nameBuf Buffer to store name of interface which this Socket object is bound to
* @param buflen Length of buffer to copy name into
*/
static inline void getIfName(PhySocket *s, char *nameBuf, int buflen)
static inline void getIfName(PhySocket* s, char* nameBuf, int buflen)
{
PhySocketImpl& sws = *(reinterpret_cast<PhySocketImpl*>(s));
if (sws.type == ZT_PHY_SOCKET_CLOSED) {
return;
}
if (s) {
memcpy(nameBuf, reinterpret_cast<PhySocketImpl *>(s)->ifname, buflen);
memcpy(nameBuf, reinterpret_cast<PhySocketImpl*>(s)->ifname, buflen);
}
}
@ -254,10 +264,14 @@ public:
* @param ifname Buffer containing name of interface that this Socket object is bound to
* @param len Length of name of interface
*/
static inline void setIfName(PhySocket *s, char *ifname, int len)
static inline void setIfName(PhySocket* s, char* ifname, int len)
{
PhySocketImpl& sws = *(reinterpret_cast<PhySocketImpl*>(s));
if (sws.type == ZT_PHY_SOCKET_CLOSED) {
return;
}
if (s) {
memcpy(&(reinterpret_cast<PhySocketImpl *>(s)->ifname), ifname, len);
memcpy(&(reinterpret_cast<PhySocketImpl*>(s)->ifname), ifname, len);
}
}
@ -270,21 +284,27 @@ public:
inline void whack()
{
#if defined(_WIN32) || defined(_WIN64)
::send(_whackSendSocket,(const char *)this,1,0);
::send(_whackSendSocket, (const char*)this, 1, 0);
#else
(void)(::write(_whackSendSocket,(PhySocket *)this,1));
(void)(::write(_whackSendSocket, (PhySocket*)this, 1));
#endif
}
/**
* @return Number of open sockets
*/
inline unsigned long count() const throw() { return _socks.size(); }
inline unsigned long count() const throw()
{
return _socks.size();
}
/**
* @return Maximum number of sockets allowed
*/
inline unsigned long maxCount() const throw() { return ZT_PHY_MAX_SOCKETS; }
inline unsigned long maxCount() const throw()
{
return ZT_PHY_MAX_SOCKETS;
}
/**
* Wrap a raw file descriptor in a PhySocket structure

4
pkg/README.md Normal file
View file

@ -0,0 +1,4 @@
Third-party packaging
=====
Builds packages for various embedded devices and appliances and platforms

10
pkg/asustor/Dockerfile Normal file
View file

@ -0,0 +1,10 @@
# vim: ft=dockerfile
FROM ubuntu:20.04
RUN apt-get update -qq && apt-get install python2.7 -y
COPY apkg-tools.py /apkg-tools.py
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

13
pkg/asustor/build.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
build_packages()
{
sudo docker run -v $(pwd):/zto ztasustor
}
build_container()
{
sudo docker build -t ztasustor . --load
}
"$@"

29
pkg/asustor/entrypoint.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/bash
ZTO_VER=$(git describe --abbrev=0 --tags)
ZTO_COMMIT=$(git rev-parse HEAD)
ZTO_DESC=$(jq -r '.desc' ../config.json)
# Clean up any pre-existing packages
find pkg/asustor -type f -name "*.apk" -exec rm -rvf {} \;
# Copy current license
cp ../../LICENSE.txt zerotier/control/license.txt
# Configure package data
tmp="config-tmp.json"
jq --arg a "$ZTO_VER" '.general.version = $a' pkg/asustor/zerotier/CONTROL/config.json > $tmp && mv $tmp pkg/asustor/zerotier/CONTROL/config.json
echo $ZTO_DESC > pkg/asustor/zerotier/CONTROL/description.txt
# Copy binaries into pkg directory
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-aarch64 pkg/asustor/zerotier/bin/zerotier-one.aarch64
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-i386 pkg/asustor/zerotier/bin/zerotier-one.i386
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-x86_64 pkg/asustor/zerotier/bin/zerotier-one.x86-64
# Package
python pkg/asustor/apkg-tools.py create pkg/asustor/zerotier
rm -rf output/asustor
mkdir -p output/asustor
mv ./*.apk output/asustor
# Show output product
cat pkg/asustor/zerotier/CONTROL/config.json
tree output/asustor

View file

@ -0,0 +1,15 @@
{
"general": {
"package": "zerotier",
"name": "ZeroTier",
"version": "1.8.6",
"depends": [],
"conflicts": [],
"developer": "ZeroTier, Inc.",
"maintainer": "ZeroTier, Inc.",
"email": "support@zerotier.com",
"website": "http://www.zerotier.com/",
"architecture": "any",
"firmware": "2.1.0"
}
}

View file

@ -0,0 +1 @@
Securely connect any device, anywhere.

Binary file not shown.

After

(image error) Size: 144 KiB

View file

@ -0,0 +1,149 @@
-----------------------------------------------------------------------------
Business Source License 1.1
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
"Business Source License" is a trademark of MariaDB Corporation Ab.
-----------------------------------------------------------------------------
Parameters
Licensor: ZeroTier, Inc.
Licensed Work: ZeroTier Network Virtualization Engine 1.4.4
The Licensed Work is (c)2019 ZeroTier, Inc.
Additional Use Grant: You may make use of the Licensed Work, provided you
do not use it in any of the following ways:
* Sell hosted ZeroTier services as a "SaaS" Product
(1) Operate or sell access to ZeroTier root servers,
network controllers, or authorization key or certificate
generation components of the Licensed Work as a
for-profit service, regardless of whether the use of
these components is sold alone or is bundled with other
services. Note that this does not apply to the use of
ZeroTier behind the scenes to operate a service not
related to ZeroTier network administration.
* Create Non-Open-Source Commercial Derviative Works
(2) Link or directly include the Licensed Work in a
commercial or for-profit application or other product
not distributed under an Open Source Initiative (OSI)
compliant license. See: https://opensource.org/licenses
(3) Remove the name, logo, copyright, or other branding
material from the Licensed Work to create a "rebranded"
or "white labeled" version to distribute as part of
any commercial or for-profit product or service.
* Certain Government Uses
(4) Use or deploy the Licensed Work in a government
setting in support of any active government function
or operation with the exception of the following:
physical or mental health care, family and social
services, social welfare, senior care, child care, and
the care of persons with disabilities.
Change Date: 2025-01-01
Change License: Apache License version 2.0 as published by the Apache
Software Foundation
https://www.apache.org/licenses/
Alternative Licensing
If you would like to use the Licensed Work in any way that conflicts with
the stipulations of the Additional Use Grant, contact ZeroTier, Inc. to
obtain an alternative commercial license.
Visit us on the web at: https://www.zerotier.com/
Notice
The Business Source License (this document, or the "License") is not an Open
Source license. However, the Licensed Work will eventually be made available
under an Open Source License, as stated in this License.
For more information on the use of the Business Source License for ZeroTier
products, please visit our pricing page which contains license details and
and license FAQ: https://zerotier.com/pricing
For more information on the use of the Business Source License generally,
please visit the Adopting and Developing Business Source License FAQ at
https://mariadb.com/bsl-faq-adopting.
-----------------------------------------------------------------------------
Business Source License 1.1
Terms
The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited
production use.
Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.
If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.
-----------------------------------------------------------------------------
MariaDB hereby grants you permission to use this Licenses text to license
your works, and to refer to it using the trademark "Business Source License",
as long as you comply with the Covenants of Licensor below.
Covenants of Licensor
In consideration of the right to use this Licenses text and the "Business
Source License" name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:
1. To specify as the Change License the GPL Version 2.0 or any later version,
or a license that is compatible with GPL Version 2.0 or a later version,
where "compatible" means that software provided under the Change License can
be included in a program with software provided under GPL Version 2.0 or a
later version. Licensor may specify additional Change Licenses without
limitation.
2. To either: (a) specify an additional grant of rights to use that does not
impose any additional restriction on the right granted in this License, as
the Additional Use Grant; or (b) insert the text "None".
3. To specify a Change Date.
4. Not to modify this License in any other way.

View file

@ -0,0 +1,24 @@
#!/bin/sh
APKG_PKG_DIR=/usr/local/AppCentral/zerotier
case "$APKG_PKG_STATUS" in
install)
modprobe tun
mkdir -p /usr/local/bin
mv ${APKG_PKG_DIR}/bin/zerotier-one.${AS_NAS_ARCH} ${APKG_PKG_DIR}/bin/zerotier-one
ln -s ${APKG_PKG_DIR}/bin/zerotier-one /usr/local/bin/zerotier-cli
ln -s ${APKG_PKG_DIR}/bin/zerotier-one /usr/local/bin/zerotier-idtool
ln -s $APKG_PKG_DIR/data /var/lib/zerotier-one
;;
upgrade)
# post upgrade script here (restore data)
# cp -af $APKG_TEMP_DIR/* $APKG_PKG_DIR/etc/.
;;
*)
;;
esac
exit 0

View file

@ -0,0 +1,3 @@
#!/bin/sh
rm -rf /var/lib/zerotier-one/

View file

View file

View file

@ -0,0 +1,26 @@
#!/bin/sh
. /etc/script/lib/command.sh
APKG_PKG_DIR=/usr/local/AppCentral/zerotier
case $1 in
start)
modprobe tun
# start script here
$APKG_PKG_DIR/bin/zerotier-one $APKG_PKG_DIR/data -d
;;
stop)
# stop script here
pkill zerotier
;;
*)
echo "usage: $0 {start|stop}"
exit 1
;;
esac
exit 0

View file

@ -0,0 +1 @@
2.0

View file

@ -0,0 +1,58 @@
<div class="header">
<h2 id="zerotier">ZeroTier</h2>
</div>
<p>Welcome! ZeroTier is a peer-to-peer encrypted virtual networking solution that enables you to create Local Area Networks with static IP assignments for all of your devices. Access your NAS from anywhere in the world with a single IP and without the need of cloud services backhauling your traffic. To use the CLI:</p>
<h2 id="using-the-cli-via-ssh-">Using the CLI via SSH:</h2>
<ol>
<li><p>Using your (admin) account, enable SSH:</p>
<ul>
<li><strong><code>Services</code></strong> -&gt; <strong><code>Terminal</code></strong> -&gt; <strong><code>Enable SSH</code></strong> (set to port <code>22</code>)</li>
</ul>
</li>
<li><p>From a computer, open a terminal and SSH into your NAS device:</p>
<ul>
<li><strong><code>ssh admin@your_nas_device_lan_ip</code></strong></li>
</ul>
</li>
<li><p>Create account and network ID at <a href="https://my.zerotier.com">my.zerotier.com</a></p>
<ul>
<li><em>Note: This account is merely to administer your network. Your traffic is not handled by ZeroTier except in the case where a direct connection cannot be established. This is a courtesy service we offer for free that you can disable if you&#39;d like. In any case, your traffic is <a href="https://www.zerotier.com/manual/#2_1_3">fully encrypted</a> end-to-end.</em></li>
</ul>
</li>
<li><p>Join your device to the network:</p>
<ul>
<li><strong><code>zerotier-cli join your_network_id</code></strong></li>
</ul>
</li>
<li><p>Use <a href="https://my.zerotier.com">my.zerotier.com</a> to authorize your NAS device to join your network.</p>
</li>
</ol>
<h2 id="help-and-support-in-order-of-relevance-">Help and support (in order of relevance)</h2>
<ul>
<li>Github Repository: <a href="https://github.com/zerotier/ZeroTierNAS/issues">https://github.com/zerotier/ZeroTierNAS</a></li>
<li>Forums: <a href="https://discuss.zerotier.com">https://discuss.zerotier.com</a></li>
<li>Contact: <a href="mailto:support@zerotier.com">support@zerotier.com</a></li>
</ul>
<div class="footer">
<h2 id="zerotier"><h2>
</div>
<style>
.header {
padding: 1px;
text-align: center;
background: #F2B464;
color: black;
font-size: 30px;
}
.footer {
padding: 1px;
text-align: center;
background: #F2B464;
color: black;
font-size: 30px;
}
</style>

6
pkg/config.json Normal file
View file

@ -0,0 +1,6 @@
{
"version": "1.8.7",
"rev": "1",
"desc": "Securely connect any device, anywhere.",
"email": "support@zerotier.com"
}

8
pkg/qnap/Dockerfile Normal file
View file

@ -0,0 +1,8 @@
# vim: ft=dockerfile
FROM ubuntu:20.04
# COPY zerotier/qbuild /qbuild
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

13
pkg/qnap/build.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
build_packages()
{
sudo docker run -v $(pwd):/zto ztqnap
}
build_container()
{
sudo docker build -t ztqnap . --load
}
"$@"

76
pkg/qnap/entrypoint.sh Executable file
View file

@ -0,0 +1,76 @@
#!/bin/bash
ZTO_VER=$(git describe --abbrev=0 --tags)
ZTO_COMMIT=$(git rev-parse HEAD)
ZTO_DESC=$(jq -r '.desc' ../config.json)
mkdir -p pkg/qnap/zerotier/arm_64
mkdir -p pkg/qnap/zerotier/arm_x09
mkdir -p pkg/qnap/zerotier/arm_x10
mkdir -p pkg/qnap/zerotier/arm_x12
mkdir -p pkg/qnap/zerotier/arm_x19
mkdir -p pkg/qnap/zerotier/arm_x31
mkdir -p pkg/qnap/zerotier/arm_x41
mkdir -p pkg/qnap/zerotier/x86_64
mkdir -p pkg/qnap/zerotier/x86
mkdir -p pkg/qnap/zerotier/x86_ce53xx
cat > pkg/qnap/zerotier/qpkg.cfg <<- EOM
# Update package config
# Name of the packaged application.
QPKG_NAME="zerotier"
# Name of the display application.
QPKG_DISPLAY_NAME="ZeroTier"
# Version of the packaged application.
QPKG_VER="$ZTO_VER"
# Author or maintainer of the package
QPKG_AUTHOR="ZeroTier, Inc."
# License for the packaged application
QPKG_LICENSE="BUSL-1.1"
# One-line description of the packaged application
QPKG_SUMMARY="$ZTO_DESC"
# Preferred number in start/stop sequence.
QPKG_RC_NUM="101"
# Init-script used to control the start and stop of the installed application.
QPKG_SERVICE_PROGRAM="zerotier.sh"
# Specifies any packages required for the current package to operate.
QPKG_REQUIRE="QVPN Service"
# Specifies what packages cannot be installed if the current package
# is to operate properly.
#QPKG_CONFLICT="Python, OPT/sed"
# Name of configuration file (multiple definitions are allowed).
#QPKG_CONFIG="myApp.conf"
#QPKG_CONFIG="/etc/config/myApp.conf"
# Port number used by service program.
QPKG_SERVICE_PORT="9993"
# Minimum QTS version requirement
QTS_MINI_VERSION="4.1.0"
# Maximum QTS version requirement
QTS_MAX_VERSION="5.0.0"
# Location of icons for the packaged application.
QDK_DATA_DIR_ICONS="icons"
EOM
# Copy binaries into pkg directory
# See: https://github.com/qnap-dev/QDK
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-aarch64 pkg/qnap/zerotier/arm_64/zerotier-one
#cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-armv5tejl pkg/qnap/zerotier/arm-x09/zerotier-one
#cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-armv5tel pkg/qnap/zerotier/arm-x19/zerotier-one
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-armhf pkg/qnap/zerotier/arm-x31/zerotier-one
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-armhf pkg/qnap/zerotier/arm-x41/zerotier-one
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-i386 pkg/qnap/zerotier/x86/zerotier-one
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-x86_64 pkg/qnap/zerotier/x86_64/zerotier-one
cp -vf output/static/zerotier-one.${ZTO_VER}.alpine-i386 pkg/qnap/zerotier/x86_ce53xx/zerotier-one
rm -rf output/qnap/*
pushd pkg/qnap/zerotier
./qbuild #--build-arch arm-x31
cp -f build/* ../../../output/qnap
# Show output product
cd popd

3
pkg/qnap/qdk.conf Normal file
View file

@ -0,0 +1,3 @@
QDK_VERSION=2.3.11
QDK_PATH_P=`pwd | awk 'BEGIN { FS = "QDK" } ; { print $1 }'`
QDK_PATH="${QDK_PATH_P}/QDK"

View file

@ -0,0 +1,7 @@
.PHONY: all
all:
@$(CC) -o qpkg_encrypt qpkg_encrypt.c
clean:
rm -rf /bin/qpkg_encrypt

View file

View file

View file

View file

View file

View file

View file

View file

View file

Binary file not shown.

After

(image error) Size: 2.7 KiB

Binary file not shown.

After

(image error) Size: 2.7 KiB

Binary file not shown.

After

(image error) Size: 1.9 KiB

View file

@ -0,0 +1,158 @@
######################################################################
# List of available definitions (it's not necessary to uncomment them)
######################################################################
###### Command definitions #####
#CMD_AWK="/bin/awk"
#CMD_CAT="/bin/cat"
#CMD_CHMOD="/bin/chmod"
#CMD_CHOWN="/bin/chown"
#CMD_CP="/bin/cp"
#CMD_CUT="/bin/cut"
#CMD_DATE="/bin/date"
#CMD_ECHO="/bin/echo"
#CMD_EXPR="/usr/bin/expr"
#CMD_FIND="/usr/bin/find"
#CMD_GETCFG="/sbin/getcfg"
#CMD_GREP="/bin/grep"
#CMD_GZIP="/bin/gzip"
#CMD_HOSTNAME="/bin/hostname"
#CMD_LN="/bin/ln"
#CMD_LOG_TOOL="/sbin/log_tool"
#CMD_MD5SUM="/bin/md5sum"
#CMD_MKDIR="/bin/mkdir"
#CMD_MV="/bin/mv"
#CMD_RM="/bin/rm"
#CMD_RMDIR="/bin/rmdir"
#CMD_SED="/bin/sed"
#CMD_SETCFG="/sbin/setcfg"
#CMD_SLEEP="/bin/sleep"
#CMD_SORT="/usr/bin/sort"
#CMD_SYNC="/bin/sync"
#CMD_TAR="/bin/tar"
#CMD_TOUCH="/bin/touch"
#CMD_WGET="/usr/bin/wget"
#CMD_WLOG="/sbin/write_log"
#CMD_XARGS="/usr/bin/xargs"
#CMD_7Z="/usr/local/sbin/7z"
#
###### System definitions #####
#SYS_EXTRACT_DIR="$(pwd)"
#SYS_CONFIG_DIR="/etc/config"
#SYS_INIT_DIR="/etc/init.d"
#SYS_STARTUP_DIR="/etc/rcS.d"
#SYS_SHUTDOWN_DIR="/etc/rcK.d"
#SYS_RSS_IMG_DIR="/home/httpd/RSS/images"
#SYS_QPKG_DATA_FILE_GZIP="./data.tar.gz"
#SYS_QPKG_DATA_FILE_BZIP2="./data.tar.bz2"
#SYS_QPKG_DATA_FILE_7ZIP="./data.tar.7z"
#SYS_QPKG_DATA_CONFIG_FILE="./conf.tar.gz"
#SYS_QPKG_DATA_MD5SUM_FILE="./md5sum"
#SYS_QPKG_DATA_PACKAGES_FILE="./Packages.gz"
#SYS_QPKG_CONFIG_FILE="$SYS_CONFIG_DIR/qpkg.conf"
#SYS_QPKG_CONF_FIELD_QPKGFILE="QPKG_File"
#SYS_QPKG_CONF_FIELD_NAME="Name"
#SYS_QPKG_CONF_FIELD_VERSION="Version"
#SYS_QPKG_CONF_FIELD_ENABLE="Enable"
#SYS_QPKG_CONF_FIELD_DATE="Date"
#SYS_QPKG_CONF_FIELD_SHELL="Shell"
#SYS_QPKG_CONF_FIELD_INSTALL_PATH="Install_Path"
#SYS_QPKG_CONF_FIELD_CONFIG_PATH="Config_Path"
#SYS_QPKG_CONF_FIELD_WEBUI="WebUI"
#SYS_QPKG_CONF_FIELD_WEBPORT="Web_Port"
#SYS_QPKG_CONF_FIELD_SERVICEPORT="Service_Port"
#SYS_QPKG_CONF_FIELD_SERVICE_PIDFILE="Pid_File"
#SYS_QPKG_CONF_FIELD_AUTHOR="Author"
#SYS_QPKG_CONF_FIELD_RC_NUMBER="RC_Number"
## The following variables are assigned values at run-time.
#SYS_HOSTNAME=$($CMD_HOSTNAME)
## Data file name (one of SYS_QPKG_DATA_FILE_GZIP, SYS_QPKG_DATA_FILE_BZIP2,
## or SYS_QPKG_DATA_FILE_7ZIP)
#SYS_QPKG_DATA_FILE=
## Base location.
#SYS_QPKG_BASE=""
## Base location of QPKG installed packages.
#SYS_QPKG_INSTALL_PATH=""
## Location of installed software.
#SYS_QPKG_DIR=""
## If the QPKG should be enabled or disabled after the installation/upgrade.
#SYS_QPKG_SERVICE_ENABLED=""
## Architecture of the device the QPKG is installed on.
#SYS_CPU_ARCH=""
## Name and location of system shares
#SYS_PUBLIC_SHARE=""
#SYS_PUBLIC_PATH=""
#SYS_DOWNLOAD_SHARE=""
#SYS_DOWNLOAD_PATH=""
#SYS_MULTIMEDIA_SHARE=""
#SYS_MULTIMEDIA_PATH=""
#SYS_RECORDINGS_SHARE=""
#SYS_RECORDINGS_PATH=""
#SYS_USB_SHARE=""
#SYS_USB_PATH=""
#SYS_WEB_SHARE=""
#SYS_WEB_PATH=""
## Path to ipkg or opkg package tool if installed.
#CMD_PKG_TOOL=
#
######################################################################
# All package specific functions shall call 'err_log MSG' if an error
# is detected that shall terminate the installation.
######################################################################
######################################################################
# Define any package specific operations that shall be performed when
# the package is removed.
######################################################################
#PKG_PRE_REMOVE="{
#}"
PKG_MAIN_REMOVE="{
rm -rf /usr/sbin/zerotier-cli
# all identity files are stored in the Install_Path and will be removed automatically
}"
#PKG_POST_REMOVE="{
#}"
######################################################################
# Define any package specific initialization that shall be performed
# before the package is installed.
######################################################################
pkg_init()
{
modprobe tun
}
######################################################################
# Define any package specific requirement checks that shall be
# performed before the package is installed.
######################################################################
#pkg_check_requirement()
#{
#}
######################################################################
# Define any package specific operations that shall be performed when
# the package is installed.
######################################################################
#pkg_pre_install()
#{
# log "pkg_pre_install"
#}
#pkg_install()
#{
# log "pkg_install"
#}
pkg_post_install()
{
log $SYS_QPKG_INSTALL_PATH
modprobe tun
}

View file

@ -0,0 +1,99 @@
# Update package config
# Name of the packaged application.
QPKG_NAME="zerotier"
# Name of the display application.
QPKG_DISPLAY_NAME="ZeroTier"
# Version of the packaged application.
QPKG_VER="1.8.4"
# Author or maintainer of the package
QPKG_AUTHOR="ZeroTier, Inc."
# License for the packaged application
QPKG_LICENSE="BUSL-1.1"
# One-line description of the packaged application
QPKG_SUMMARY="Securely connect any device, anywhere."
# Preferred number in start/stop sequence.
QPKG_RC_NUM="101"
# Init-script used to control the start and stop of the installed application.
QPKG_SERVICE_PROGRAM="zerotier.sh"
# Specifies any packages required for the current package to operate.
QPKG_REQUIRE="QVPN Service"
# Specifies what packages cannot be installed if the current package
# is to operate properly.
#QPKG_CONFLICT="Python, OPT/sed"
# Name of configuration file (multiple definitions are allowed).
#QPKG_CONFIG="myApp.conf"
#QPKG_CONFIG="/etc/config/myApp.conf"
# Port number used by service program.
QPKG_SERVICE_PORT="9993"
# Location of file with running service's PID
#QPKG_SERVICE_PIDFILE="/var/lib/zerotier-one/zerotier-one.pid"
# Relative path to web interface
#QPKG_WEBUI=""
# Port number for the web interface.
#QPKG_WEB_PORT=""
# Port number for the SSL web interface.
#QPKG_WEB_SSL_PORT=""
# Use QTS HTTP Proxy and set Proxy_Path in the qpkg.conf.
# When the QPKG has its own HTTP service port, and want clients to connect via QTS HTTP port (default 8080).
# Usually use this option when the QPKG need to connect via myQNAPcloud service.
#QPKG_USE_PROXY="1"
#QPKG_PROXY_PATH="/qpkg_name"
#Desktop Application (since 4.1)
# Set value to 1 means to open the QPKG's Web UI inside QTS desktop instead of new window.
#QPKG_DESKTOP_APP="1"
# Desktop Application Window default inner width (since 4.1) (not over 1178)
#QPKG_DESKTOP_APP_WIN_WIDTH=""
# Desktop Application Window default inner width (since 4.1) (not over 600)
#QPKG_DESKTOP_APP_WIN_HEIGHT=""
# Minimum QTS version requirement
QTS_MINI_VERSION="4.1.0"
# Maximum QTS version requirement
QTS_MAX_VERSION="5.0.0"
# Select volume
# 1: support installation
# 2: support migration
# 3 (1+2): support both installation and migration
#QPKG_VOLUME_SELECT="0"
# Set timeout for QPKG enable and QPKG disable (since 4.1.0)
# Format in seconds (enable, disable)
#QPKG_TIMEOUT="10,30"
# Visible setting for the QPKG that has web UI, show this QPKG on the Main menu of
# 1(default): administrators, 2: all NAS users.
#QPKG_VISIBLE="2"
# Location of the chroot environment (only TS-x09)
#QPKG_ROOTFS=""
# Init-script used to controls the start and stop of the
# installed application (only TS-x09)
#QPKG_SERVICE_PROGRAM_CHROOT=""
# Location of icons for the packaged application.
QDK_DATA_DIR_ICONS="icons"
# Location of files specific to arm-x09 packages.
#QDK_DATA_DIR_X09="arm-x09"
# Location of files specific to arm-x19 packages.
#QDK_DATA_DIR_X19="arm-x19"
# Location of files specific to arm-x31 packages.
#QDK_DATA_DIR_X31="arm-x31"
# Location of files specific to arm-x41 packages.
#QDK_DATA_DIR_X41="arm_al"
# Location of files specific to x86 packages.
#QDK_DATA_DIR_X86="x86"
# Location of files specific to x86 (64-bit) packages.
#QDK_DATA_DIR_X86_64="x86_64"
# Location of files common to all architectures.
#QDK_DATA_DIR_SHARED="shared"
# Location of configuration files.
#QDK_DATA_DIR_CONFIG="config"
# Name of local data package.
#QDK_DATA_FILE=""
# Name of extra package (multiple definitions are allowed).
#QDK_EXTRA_FILE=""

View file

Binary file not shown.

After

(image error) Size: 1.8 KiB

Binary file not shown.

After

(image error) Size: 1.8 KiB

Binary file not shown.

After

(image error) Size: 2.3 KiB

View file

@ -0,0 +1,34 @@
#!/bin/sh
CONF=/etc/config/qpkg.conf
QPKG_NAME="zerotier"
QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}`
APACHE_ROOT=/share/`/sbin/getcfg SHARE_DEF defWeb -d Qweb -f /etc/config/def_share.info`
case "$1" in
start)
modprobe tun
ln -s $QPKG_ROOT/zerotier-one /usr/sbin/zerotier-cli
ln -s $QPKG_ROOT/zerotier-one /usr/bin/zerotier-cli
ln -s $QPKG_ROOT /var/lib/zerotier-one
ENABLED=$(/sbin/getcfg $QPKG_NAME Enable -u -d FALSE -f $CONF)
if [ "$ENABLED" != "TRUE" ]; then
echo "$QPKG_NAME is disabled."
exit 1
fi
$QPKG_ROOT/zerotier-one $QPKG_ROOT -d
;;
stop)
killall zerotier-one
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0

Some files were not shown because too many files have changed in this diff Show more